Side by side
npmvspnpm
What is the difference between npm and pnpm?
Updated 3 min read8 differences
In short
npm copies every package into each project's flat node_modules; pnpm keeps one copy in a shared store, links it in and blocks undeclared imports.
npm
Node Package Manager
npm is Node.js's default package manager and the world's largest software registry; it downloads a project's dependencies and tracks their versions.
Read the page on npmpnpm
Performant npm
pnpm is a fast, disk-efficient JavaScript package manager that keeps one copy of each package version in a shared store and links it into projects.
Read the page on pnpmnpm and pnpm compared
| Aspect | npm | pnpm |
|---|---|---|
| Disk usage | A full copy of every package in each project | One copy per version in a shared store, hard-linked into projects |
| Folder layout | Flat: dependencies are hoisted to the top level | Strict: only direct dependencies at the top, linked into .pnpm |
| Undeclared imports | Often work by accident (phantom dependencies) | Fail right away |
| Lockfile | package-lock.json | pnpm-lock.yaml |
| Repeat installs | Unpacks and copies every package again from its cache | Links files already in the store; usually faster |
| Install scripts | Dependencies' scripts run by default | Blocked since version 10 until you approve them |
| Monorepos | Workspaces with --workspace flags | pnpm-workspace.yaml with --filter |
| Setup | Ships with Node.js | Installed separately, often with Corepack or npm install -g pnpm |
The difference, explained
npm and pnpm are both JavaScript package managers that read the same package.json and install from the same npm registry. npm is the default and ships with every installation of Node.js. pnpm, short for performant npm, is a separate tool built to save disk space, speed up installs and keep dependencies strict; anything published to npm installs with it.
The difference is where the files go. npm copies every dependency into each project's node_modules and hoists them into one flat folder, so ten projects that use React hold ten copies of it, and your code can import a package it never declared just because a dependency pulled it in: a phantom dependency. pnpm saves each file once in a content-addressable store and hard-links it into projects, and its node_modules holds only your direct dependencies, as symbolic links into node_modules/.pnpm, so undeclared imports fail right away.
The commands are close: pnpm add react for npm install react, pnpm dlx for npx, and pnpm import turns an existing package-lock.json into pnpm-lock.yaml. pnpm is popular for monorepos, with a pnpm-workspace.yaml file and a --filter flag that runs commands in only some packages. It is also stricter about security: since version 10 it doesn't run dependencies' install scripts unless you approve them, while npm runs them by default unless you pass --ignore-scripts.
A common misconception is that pnpm is a fork of npm or a separate registry; it is neither, only a different way of laying packages out on disk. Its strictness sometimes breaks older packages that relied on hoisting, which is a hidden bug in those packages rather than in pnpm, and pnpm has hoisting settings that loosen the layout for such cases. As with any package manager, a project should use one and commit only its lockfile.
Which one should you use?
Choose npm when…
- You want the default tool that comes with Node.js, with nothing else to install.
- Your host, CI images or tutorials assume npm and
package-lock.json. - Older tools in the project expect a flat
node_modulesfolder.
Choose pnpm when…
- Disk space or install time matters, across many projects or in CI.
- You want undeclared imports to fail instead of working by accident.
- You run a monorepo and want
--filterand workspace links. - You want dependencies' install scripts blocked until you approve them.
Installing Express, then importing a package that was never declared
# npm: dependencies are hoisted into one flat folder
npm install express
ls node_modules
# accepts body-parser ... debug ... express ...
# debug is not in package.json, but this works
node -e "require('debug')"# pnpm: only declared dependencies sit at the top
pnpm add express
ls node_modules
# express (a link into node_modules/.pnpm)
# debug was never declared, so this fails
node -e "require('debug')"
# Error: Cannot find module 'debug'Readers ask
Is pnpm faster than npm?
On repeat installs, usually yes: files already in the store are linked instead of unpacked and copied again. The gap depends on the project and on whether the store is already filled, so first installs on a clean machine are closer.
Does pnpm work with every npm package?
Almost all of them, since it installs from the same registry. A few older packages import dependencies they never declared and break under pnpm's strict layout; its hoisting settings make the layout looser for those.
How do I switch a project from npm to pnpm?
Run pnpm import to create pnpm-lock.yaml from package-lock.json, delete the old lockfile and node_modules, then run pnpm install. Record pnpm in the packageManager field so everyone uses the same tool.