The npm uninstall <package> command removes a package from your node_modules folder and deletes its entry from package.json and package-lock.json, but it does not automatically remove transitive dependencies that only that package required, which is why an uninstall can leave risk behind if you don't verify. Removing a dependency is one of the most direct things you can do to shrink your attack surface — every package you drop is code that can no longer be compromised on your behalf. This guide covers the command's variants, the cleanup it does and doesn't do, and how to confirm that what you uninstalled is genuinely gone.
The basic command and its aliases
The canonical form:
npm uninstall lodash
This removes lodash from node_modules and strips it from the dependencies section of package.json. npm also accepts several aliases that do exactly the same thing — npm remove, npm rm, npm r, npm un, and npm unlink all map to uninstall — so you'll see them used interchangeably in scripts and documentation.
You can remove several packages in one call:
npm uninstall lodash moment request
By default npm updates package.json. In older npm versions you needed --save to persist the change; since npm 5 that's the default behavior, so a plain npm uninstall writes the manifest for you.
Removing dev, peer, and global packages
Where a package lives changes the flag you need. For a devDependency, the removal works the same but you can be explicit:
npm uninstall --save-dev eslint
For a globally installed package — a CLI tool installed with npm install -g — add the global flag:
npm uninstall -g create-react-app
Forgetting -g is a common source of confusion: running npm uninstall create-react-app inside a project won't touch the global copy, and you'll wonder why the command still resolves. Check what's installed globally with npm ls -g --depth=0.
What uninstall leaves behind
Here's the part that matters for security. When you uninstall a package, npm removes that package and any of its dependencies that nothing else needs. In practice, the cleanup is usually complete in modern npm — but there are real edge cases where cruft or risk survives.
First, the lockfile. package-lock.json is updated on uninstall, but if you're auditing a repository where dependencies were removed carelessly in the past, the lockfile can still reference packages that no longer appear in package.json. Running a clean reinstall reconciles them:
rm -rf node_modules package-lock.json
npm install
Second, node_modules drift. If team members remove packages by hand-editing package.json without running uninstall, or across merges, the on-disk node_modules can hold packages the manifest no longer declares. The rm -rf node_modules && npm install reset above is the reliable way to make disk match manifest.
Third — and this is the security-relevant one — uninstalling a package doesn't retroactively review what that package did while it was installed. If the package you're removing had a malicious install script, that script already ran when it was first installed. Uninstalling stops future exposure; it doesn't undo past execution.
Confirming a package is really gone
Don't assume the uninstall worked — verify. The most direct check is to look for the package in your resolved tree:
npm ls <package>
If it prints "(empty)" or errors that the package isn't found, it's gone from your dependency graph. If it still appears, something else depends on it transitively — which is expected and fine if that dependency is legitimate, but worth understanding.
That transitive case is the trap. Suppose you uninstall a package to remove a vulnerable version of a library it pulled in. If a different dependency also requires that same vulnerable library, the vulnerable copy stays in your tree even though your direct uninstall succeeded. npm ls <vulnerable-package> reveals every path that still reaches it, so you know whether the removal actually eliminated the risk or just one route to it.
Uninstalling to reduce your attack surface
Removing unused dependencies is underrated security hygiene. Every package in your tree is code that runs with your application's privileges, can define install-time scripts, and can be compromised in a supply-chain attack. Dependencies that no code actually imports are pure downside — risk with no benefit.
Find them with a tool like depcheck:
npx depcheck
It reports packages declared in package.json that no source file imports. Review the list (build tools and CLI-only packages sometimes show as false positives), then uninstall the genuinely unused ones. Doing this periodically keeps your node_modules lean and your exposure smaller.
When you uninstall to remove a vulnerable dependency specifically, confirm the fix stuck. A software composition analysis tool such as Safeguard can scan the resulting tree and confirm the flagged CVE is truly gone rather than surviving through another path — the transitive case that a manual uninstall easily misses. Our SCA overview covers how that verification works, and for a broader routine see our npm security best practices.
FAQ
Does npm uninstall remove transitive dependencies?
It removes the target package and any dependencies that were required only by that package. If another installed package also depends on one of those transitive packages, that shared dependency stays in your tree — which is correct behavior, but it means an uninstall doesn't always eliminate a vulnerable transitive package.
What's the difference between npm uninstall and npm remove?
None. npm remove, npm rm, npm r, npm un, and npm unlink are all aliases for npm uninstall and behave identically. Use whichever reads best in your context.
How do I uninstall a global npm package?
Add the -g flag: npm uninstall -g <package>. Without it, npm looks only in the current project and won't touch the globally installed copy. List global packages with npm ls -g --depth=0 to confirm what's installed.
How do I confirm a package is fully removed?
Run npm ls <package> — an empty or not-found result means it's gone from your dependency graph. For a clean slate that reconciles node_modules with the lockfile, delete both node_modules and package-lock.json and run npm install again.