Find the dead dependencies in your lockfile — and what replaced them
dead-deps reads your lockfile, works out which dependencies have genuinely stopped being maintained, and tells you what the ecosystem moved to instead. Every verdict comes with sources you can check.
npx dead-deps
No install, no account, no upload. It reads package-lock.json, pnpm-lock.yaml, yarn.lock or package.json, asks public registry indexes about each dependency, and prints the ones that have stopped moving — worst first. It exits 1 when something is flagged, so it works as a CI gate.
What it actually tells you
Most tools that look at dependency health return a number. A number cannot be acted on. dead-deps returns a state and a successor:
- The state —
active,stable-complete,low-activity,unmaintained,deprecated,abandonedorhijack-risk. Never a boolean, because "no commits in three years" describes both an abandoned framework and a finished twelve-line utility. - The evidence — the registry deprecation notice, the archived repository, the release cadence, the advisory. Each one is a line you can verify yourself. An unsourced verdict is a bug.
- The successor — drawn from the 81-row curated dataset indexed below, so the answer to "so what do I use?" is in the same output.
A quiet package is not a dead package
The naive detector reads the date of the last release and calls anything old dead. It fails immediately, because the npm graph rests on tiny packages that were finished years ago — once, inherits, wrappy, util-deprecate. They do one small thing correctly, so nobody commits to them, and on release date alone they are indistinguishable from abandonware. A tool that tells you to migrate off inherits has not found a problem; it has become one.
So stable-complete is its own verdict, guarded by its own rules, and the false-positive rate over a hand-labelled corpus of finished packages is measured separately. That is the part worth reading: how verdicts are produced, and the stable-complete guard.
The succession dataset
Knowing a package is dead is half an answer. The other half — what replaced it — cannot be computed, so it is curated by hand. Each of the 81 rows below records how the succession happened (a community fork, a rename, an unrelated replacement, functionality absorbed into the platform, a successor the maintainers declared themselves, or a reimplementation), whether the successor is a drop-in, and at least one primary source.
Roughly a fifth of real successions do not point at a package at all. left-pad was absorbed by String.prototype.padStart; q by native promises. "Delete the dependency, the platform does this now" is a better answer than any package name, so the dataset can say it.
Rows about small, finished, still-working packages are deliberately excluded. Being quiet is not being abandoned. Read the schema and inclusion rules before proposing a row.
Every package covered (81)
One page per package, each answering the question people actually type: is it still maintained, what replaced it, and is the replacement a drop-in.
A
- @angular/httpAbsorbed elsewhere: @angular/common · unmaintained since May 2019
- angularReimplementation: @angular/core · unmaintained since January 2022
- apollo-serverRenamed package: @apollo/server · unmaintained since November 2023
- apollo-server-expressRenamed package: @apollo/server · unmaintained since November 2023
B
- babel-eslintRenamed package: @babel/eslint-parser · unmaintained since February 2020
- babel-preset-es2015Successor named by the maintainers: @babel/preset-env · unmaintained since April 2017
- bluebirdReplaced by Promise (native) in the platform · unmaintained since November 2019
- boomRenamed package: @hapi/boom · unmaintained since November 2018
C
- casperjsReplacement project: playwright · unmaintained since March 2018
- codecovNo direct successor · unmaintained since July 2021
- colorsMaintained fork: @colors/colors · unmaintained since January 2022
- connect-multipartyReplacement project: multer · unmaintained since September 2018
- create-react-appSuccessor named by the maintainers: vite · unmaintained since February 2025
- csurfReplacement project: csrf-csrf · unmaintained since January 2020
- cucumberRenamed package: @cucumber/cucumber · unmaintained since December 2020
- cuidReimplementation: @paralleldrive/cuid2 · unmaintained since January 2023
E
- enzymeReplacement project: @testing-library/react · unmaintained since December 2019
- eslint-loaderSuccessor named by the maintainers: eslint-webpack-plugin · unmaintained since April 2020
- eslint-plugin-nodeMaintained fork: eslint-plugin-n · unmaintained since March 2020
- event-streamNo direct successor · unmaintained since November 2018
- express-graphqlReimplementation: graphql-http · unmaintained since November 2020
- extract-text-webpack-pluginSuccessor named by the maintainers: mini-css-extract-plugin · unmaintained since February 2018
F
- fakerMaintained fork: @faker-js/faker · unmaintained since January 2022
- feathersRenamed package: @feathersjs/feathers · unmaintained since January 2018
G
- geminiSuccessor named by the maintainers: testplane · unmaintained since November 2019
- glamorousSuccessor named by the maintainers: @emotion/react · unmaintained since December 2018
- gulp-utilNo direct successor · unmaintained since December 2016
H
- @hapi/joiRenamed package: joi · unmaintained since March 2020
- hapiRenamed package: @hapi/hapi · unmaintained since February 2019
- har-validatorNo direct successor · unmaintained since July 2020
- hermioneRenamed package: testplane · unmaintained since June 2024
I
- istanbulRenamed package: nyc · unmaintained since August 2016
J
- jadeRenamed package: pug · unmaintained since June 2015
- jasmine-nodeSuccessor named by the maintainers: jasmine · unmaintained since May 2019
- jest-environment-jsdom-sixteenAbsorbed elsewhere: jest-environment-jsdom · unmaintained since April 2021
- jquery.cookieRenamed package: js-cookie · unmaintained since April 2014
- jscsSuccessor named by the maintainers: eslint · unmaintained since April 2016
K
- karmaSuccessor named by the maintainers: vitest · unmaintained since April 2023
- koa-routerRenamed package: @koa/router
L
- labRenamed package: @hapi/lab · unmaintained since February 2019
- left-padReplaced by String.prototype.padStart in the platform · unmaintained since April 2018
- lodash.getReplaced by optional chaining (?.) in the platform · unmaintained since August 2016
- lodash.isequalReplaced by node:util.isDeepStrictEqual in the platform · unmaintained since January 2017
M
- @material-ui/coreRenamed package: @mui/material · unmaintained since September 2021
- momentSuccessor named by the maintainers: luxon · unmaintained since September 2020
N
- nightmareReplacement project: playwright · unmaintained since April 2019
- node-sassReimplementation: sass · unmaintained since July 2024
- node-sass-chokidarReplacement project: sass · unmaintained since June 2022
- node-uuidRenamed package: uuid · unmaintained since March 2017
- nodeunitNo direct successor · unmaintained since July 2018
P
- parcel-bundlerRenamed package: parcel · unmaintained since March 2021
- phantomjs-prebuiltReplacement project: puppeteer · unmaintained since March 2018
- popper.jsRenamed package: @popperjs/core · unmaintained since January 2020
- postcss-cssnextSuccessor named by the maintainers: postcss-preset-env · unmaintained since May 2018
- protractorReplacement project: @playwright/test · unmaintained since April 2021
- puppeteer-firefoxAbsorbed elsewhere: puppeteer · unmaintained since January 2020
Q
- qReplaced by Promise (native) in the platform · unmaintained since October 2017
- querystringReplaced by URLSearchParams in the platform · unmaintained since February 2021
- qunitjsRenamed package: qunit · unmaintained since October 2017
R
- react-addons-test-utilsAbsorbed elsewhere: react-dom · unmaintained since April 2017
- react-testing-libraryRenamed package: @testing-library/react · unmaintained since May 2019
- recomposeAbsorbed elsewhere: react · unmaintained since October 2018
- redux-formSuccessor named by the maintainers: react-final-form · unmaintained since March 2023
- requestReplacement project: undici · unmaintained since February 2020
- request-promiseReplacement project: got · unmaintained since July 2020
- request-promise-anyReplacement project: got · unmaintained since July 2020
- request-promise-nativeReplacement project: got · unmaintained since July 2020
- rollup-plugin-babelRenamed package: @rollup/plugin-babel · unmaintained since March 2020
S
- shortidSuccessor named by the maintainers: nanoid · unmaintained since October 2020
- sinon-as-promisedAbsorbed elsewhere: sinon · unmaintained since March 2017
- snowpackSuccessor named by the maintainers: vite · unmaintained since April 2022
- socket.io-redisRenamed package: @socket.io/redis-adapter · unmaintained since May 2021
- standard-versionMaintained fork: commit-and-tag-version · unmaintained since May 2022
- stylelint-config-prettierNo direct successor · unmaintained since February 2023
- supertest-as-promisedAbsorbed elsewhere: supertest · unmaintained since November 2016
T
- tslintReplacement project: typescript-eslint · unmaintained since February 2019
U
- uglify-esMaintained fork: terser · unmaintained since February 2018
- uglifyjs-webpack-pluginSuccessor named by the maintainers: terser-webpack-plugin · unmaintained since July 2019
V
- vue-resourceSuccessor named by the maintainers: axios · unmaintained since November 2016
W
- wreckRenamed package: @hapi/wreck · unmaintained since March 2019
Z
- @zeit/next-sassAbsorbed elsewhere: next · unmaintained since March 2020
From an editor or an agent
The same engine ships as an MCP server, so an assistant working in your repository can scan the lockfile, check a single package, or look up a successor without shelling out:
npx dead-deps-mcp
It exposes scan_lockfile, check_package and find_successor over stdio. Point your MCP client at that command.