Stale dependencies accumulate vulnerabilities and painful multi-major upgrades.
A healthy routine:
npm outdatedshows Current (installed), Wanted (newest within your range) and Latest (thelatestdist-tag). Red rows are behind what your range allows.npm updatemoves everything to Wanted and updates the lockfile. Safe-ish: same majors (for^ranges).- Major upgrades are explicit:
npm install pkg@latest, then read the changelog and run the tests. Upgrade one major at a time for big frameworks. npm dedupesimplifies the tree by moving shared dependencies up so fewer duplicate versions are installed;npm find-dupespreviews it.npm install --before=2026-09-01or themin-release-agesetting (number of days) resolves only versions published before a date, which helps reproduce old installs and lets you avoid brand-new, possibly compromised releases.
Automate it: Renovate or Dependabot open pull requests for updates, group related packages, and can automerge patch updates that pass CI. Keep the lockfile in those PRs so CI tests exactly what will ship.
Transitive dependencies update through their parents' ranges. If a vulnerable transitive package is pinned by a parent you cannot upgrade, use overrides (advanced lesson).
Example
npm outdated
# Package Current Wanted Latest Location
# express 5.0.1 5.1.0 5.1.0 node_modules/express
# typescript 5.8.3 5.9.3 5.9.3 node_modules/typescript
# vitest 3.2.4 3.2.4 4.0.8 node_modules/vitest
npm update # express -> 5.1.0, typescript -> 5.9.3
npm install vitest@latest # deliberate major upgrade, then run tests
npm test
npm find-dupes # what npm dedupe would change
npm dedupe
# Only accept versions that have been public for at least 3 days
npm config set min-release-age 3 --location=projectRemember
- outdated: Current vs Wanted (in range) vs Latest
npm updatestays within ranges; majors needpkg@latestnpm deduperemoves duplicate versions- Bots (Renovate, Dependabot) keep updates small and continuous
Watch out for
- Letting dependencies rot for two years, then facing five breaking majors at once.