How to check installed npm package version
Run npm ls with the package name in the project root. It prints the installed version under every package that depends on it, and exits 1 when nothing matches. npm pkg get returns the declared range instead of a version, and node -p on the installed manifest returns what the runtime loads.
Why check this
A tester runs this the moment a bug report names a library version. The question "is the fix in the build I am testing" has one honest answer and three commands that look like they give it, so the first job is picking the command that reports the tree on disk rather than the intention in package.json.
The failure it prevents is closing a defect against the wrong copy. A dependency can be installed twice at two versions in one project, and the version a reviewer reads from the project root is not always the one the failing code path loads.
Prerequisites
- npm 7 or later. The npm ls documentation describes the tree output and the
dedupedmarker. - An installed
node_modules. Every command here reads the tree on disk, not the lockfile. - The outputs below come from this site's own repository on 2026-09-12. These are the declarations they resolve from.
{
"dependencies": {
"next": "^16.3.5"
},
"devDependencies": {
"@tailwindcss/postcss": "4.1.13",
"typescript": "5.9.2",
"vitest": "^5.0.0"
}
}
Steps
- Step 1.
Ask for one direct dependency.
npm ls typescript; echo "exit=$?"h2check@0.1.0 D:\how2check `-- typescript@5.9.2 exit=0One line under the project, one version, exit 0. This is the answer whenever the package appears once in the tree.
- Step 2.
Ask for a package that is not installed. The text and the exit code disagree about how serious this is.
npm ls express; echo "exit=$?"h2check@0.1.0 D:\how2check `-- (empty) exit=1(empty)is the answer to "is this package installed", and the answer is no. Exit 1 here reports an absent package, not a command that failed to run. - Step 3.
Ask for a package that several dependencies want.
npm ls postcssh2check@0.1.0 D:\how2check +-- @tailwindcss/postcss@4.1.13 | `-- postcss@8.5.28 +-- next@16.3.5 | `-- postcss@8.5.23 `-- vitest@5.0.0 `-- vite@8.3.0 `-- postcss@8.5.28 dedupedThree rows and two versions.
nextdeclares postcss exactly at 8.5.23, so it gets a private copy. Thededupedmarker on the third row means vite shares the 8.5.28 already installed for@tailwindcss/postcss, not that a third copy exists. - Step 4.
Compare that against the two shortcuts people reach for first.
npm pkg get dependencies.postcss devDependencies.postcss && node -p "require('postcss/package.json').version"{} 8.5.28npm pkg getreads package.json, and postcss is not declared there, so it returns an empty object.requireresolves from the project root and finds the hoisted copy, 8.5.28. Neither command knows about 8.5.23. - Step 5.
Read the version the dependent actually loads.
node -p "require('./node_modules/next/node_modules/postcss/package.json').version"8.5.23Code inside
nextresolves postcss to its nested copy before it reaches the hoisted one. A defect in 8.5.23 is present in this project while the root reports 8.5.28. - Step 6.
Get the path of every copy, with the range each dependent asked for.
npm explain postcsspostcss@8.5.23 node_modules/next/node_modules/postcss postcss@"8.5.23" from next@16.3.5 node_modules/next next@"^16.3.5" from the root project postcss@8.5.28 dev node_modules/postcss postcss@"^8.4.41" from @tailwindcss/postcss@4.1.13 node_modules/@tailwindcss/postcss dev @tailwindcss/postcss@"4.1.13" from the root project postcss@"^8.5.28" from vite@8.3.0 …Two versions, two directories, and the reason for each. The exact pin
postcss@"8.5.23"is what forced the second copy; the two caret ranges both accept 8.5.28 and share one.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| One row, one version | The package is installed once | Quote that version in the bug report. |
| Two rows with different versions | Two copies are installed at different paths | Run npm explain and name the path that the failing code resolves from. |
| A row ending in deduped | That dependent shares a copy listed earlier | Count it once. It is one directory on disk, not two. |
| (empty) with exit 1 | Nothing in the tree matches the name | Confirm the spelling, then check whether it is installed globally. |
| npm pkg get returning {} | The name is not declared in package.json | It arrived through another package. npm ls still finds it. |
| An invalid or extraneous marker | The tree does not match the lockfile | Reinstall before you trust any version on this page. |
Common mistakes
What to check next
- How to check dependency tree npm: the whole tree, when one package name is not enough.
- How to check why a package is installed: the command behind step 6, read in full.
- How to check if a npm package is installed globally: where to look when
npm lsreports(empty)and the binary still runs. - How to check if package lock is in sync: whether the tree you measured is the tree the lockfile describes.
- Pull request checklist: the other dependency checks that run on the same change.
FAQ
How do I check if a package is installed with npm?
npm ls with the package name. It exits 0 and prints the version when the package is in the tree, and exits 1 and prints (empty) when it is not. Read the exit code, because both cases print the project line above it.
What is the difference between npm ls and npm view?
npm ls reads node_modules in the current project. npm view queries the registry and reports what is published under the latest dist-tag. The second one answers a question about the internet, not about your build.
Why does npm ls print two versions of one package?
Dependents asked for ranges that no single version satisfies. Here next pinned postcss to 8.5.23 while two other packages accepted 8.5.28, so npm installed both, one at the root and one nested under next.
Which version does my own code load?
The one node -p "require('the-name/package.json').version" prints when run from your project root, because your code resolves from the same place. Code inside a dependency can resolve to a different copy.
Does npm ls read the lockfile?
No, it reads the installed tree. An invalid or extraneous marker in its output is npm saying the two disagree, which is a separate finding from the version itself.
Verified
Verified by Maks Vernynpm 10.9.8Node.js 22.23.2
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
basic5 minpublished updated Maks Verny