How to check outdated npm packages
Run npm outdated in the project root. It prints one row per package that is behind, with Current, Wanted and Latest columns, and exits 1 when the list is not empty. Wanted is the newest version your semver range allows; Latest is the newest the registry publishes.
Why check this
A tester runs this before a regression cycle is planned, because it decides how much of the suite the cycle has to cover. Three patch bumps inside a range are a smoke run. A framework major is a full pass. The command tells you which of the two you are looking at, in one screen, before anyone estimates.
The failure it prevents is a silent upgrade during the cycle. A range like ^16.4.7 lets a fresh npm install on a build agent pull a version nobody tested, so the build that ships is not the build that passed.
Prerequisites
- npm 7 or later. The npm outdated documentation describes the columns.
- An installed
node_modules. The Current column is read from disk, so a project that was never installed reports nothing useful. - To reproduce steps 2 to 4, create a directory named
outdated-demo, put this inpackage.json, and runnpm install.
{
"name": "outdated-demo",
"version": "1.0.0",
"private": true,
"dependencies": {
"dayjs": "1.11.13",
"dotenv": "16.4.7",
"postcss": "8.4.49"
}
}
Registry versions move, so a run on a later date returns higher numbers than the ones below, captured on 2026-09-12.
Steps
- Step 1.
Run the command in a real project and read the exit code with it.
npm outdated; echo "exit=$?"Package Current Wanted Latest Location Depended by @tailwindcss/postcss 4.1.13 4.1.13 4.3.3 node_modules/@tailwindcss/postcss how2check @types/node 22.18.1 22.18.1 22.20.2 node_modules/@types/node how2check @types/react 19.1.13 19.1.13 19.3.0 node_modules/@types/react how2check @types/react-dom 19.1.9 19.1.9 19.3.0 node_modules/@types/react-dom how2check cron-parser 5.4.0 5.4.0 5.10.0 node_modules/cron-parser how2check tailwindcss 4.1.13 4.1.13 4.3.3 node_modules/tailwindcss how2check tsx 4.20.6 4.20.6 4.23.13 node_modules/tsx how2check typescript 5.9.2 5.9.2 7.0.2 node_modules/typescript how2check zod 4.1.8 4.1.8 4.6.2 node_modules/zod how2check exit=1Nine rows from this site's own repository. Wanted equals Current on every one of them, because each is declared as an exact version with no range operator. TypeScript is two majors behind.
- Step 2.
Run it in the demo project, which is also pinned exactly.
npm outdatedPackage Current Wanted Latest Location Depended by dayjs 1.11.13 1.11.13 1.11.23 node_modules/dayjs outdated-demo dotenv 16.4.7 16.4.7 17.4.2 node_modules/dotenv outdated-demo postcss 8.4.49 8.4.49 8.5.28 node_modules/postcss outdated-demoSame shape as step 1. An exact pin makes Wanted meaningless, because the range admits exactly one version.
- Step 3.
Change the three ranges to carets in
package.jsonand run the command again, without reinstalling.npm outdatedPackage Current Wanted Latest Location Depended by dayjs 1.11.13 1.11.23 1.11.23 node_modules/dayjs outdated-demo dotenv 16.4.7 16.6.1 17.4.2 node_modules/dotenv outdated-demo postcss 8.4.49 8.5.28 8.5.28 node_modules/postcss outdated-demoNothing on disk moved; only the ranges did. The dotenv row now has three different numbers:
^16.4.7reaches 16.6.1 and stops at the major boundary, while the registry is on 17.4.2. - Step 4.
Ask for the packages you did not choose.
npm outdated --allPackage Current Wanted Latest Location Depended by dayjs 1.11.13 1.11.23 1.11.23 node_modules/dayjs outdated-demo dotenv 16.4.7 16.6.1 17.4.2 node_modules/dotenv outdated-demo nanoid 3.3.19 3.3.19 6.0.1 node_modules/nanoid postcss postcss 8.4.49 8.5.28 8.5.28 node_modules/postcss outdated-demoA fourth row appears.
nanoidis three majors behind and reaches the project through postcss, which is why the Depended by column sayspostcssand not the project name.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| Wanted above Current | The lockfile holds a version older than your range allows | A fresh npm install will move it. Run the suite before that happens on a build agent, not after. |
| Latest above Wanted | A major release exists outside the range | Plan it as a code change. Read the changelog, not the version number. |
| Wanted equals Current on every row | The dependency is pinned exactly | Nothing will move on its own. Every upgrade is a deliberate edit. |
| No output at all, exit 0 | Every direct dependency is at Latest | Re-run with --all before you report it; transitive packages are hidden by default. |
| A row whose Depended by is another package | A transitive dependency | You cannot bump it directly. Bump its parent, or add an overrides entry. |
Common mistakes
What to check next
- How to check npm packages for vulnerabilities: being behind and being exploitable are different findings from different commands.
- How to check if package lock is in sync: the check that decides whether Wanted will ever be installed.
- How to check installed npm package version: how to confirm the Current column against what the runtime actually loads.
- How to check dependency tree npm: where a transitive row in this report comes from.
- Pull request checklist: the other checks that run on a dependency change.
FAQ
What is the difference between Wanted and Latest?
Wanted is the highest version that satisfies the range in your package.json. Latest is the version the registry serves under the latest dist-tag. With ^16.4.7, Wanted was 16.6.1 and Latest was 17.4.2, because a caret range stops at the next major.
How do I update to the Wanted column?
npm update moves packages to Wanted and rewrites the lockfile. On the demo project npm update --dry-run planned dotenv 16.4.7 to 16.6.1, not to the latest 17.4.2. Reaching Latest means editing the range yourself and running the tests.
Why does npm outdated show nothing?
Either every direct dependency is at Latest, or the ranges are satisfied and you are looking at the default view. Run npm outdated --all to include transitive packages before concluding the tree is current.
Does npm outdated change anything on disk?
No. It reads node_modules and queries the registry. The demo in step 3 changed only the ranges in package.json, and the Current column stayed at the installed versions.
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
basic6 minpublished updated Maks Verny