npm → pnpm
The npm command you know beside its pnpm equivalent, for every common task — plus what changes when node_modules stops being flat.
Runs in your browser
| Task | npm | pnpm |
|---|---|---|
| Install everything in the lockfile | ||
| Install for CI, failing if the lockfile would change | ||
| Add a dependency | ||
| Add a dev dependency | ||
| Add without a version range | ||
| Install globally | ||
| Remove a dependency | ||
| Update within the declared ranges | ||
| List packages behind their latest | ||
| Run a script from package.json | ||
| Run a package binary without installing it | ||
| Start a new package | ||
| Explain why a package is installed | ||
| Check dependencies for advisories | ||
| Add a dependency to one workspace |
The short version
| npm | pnpm |
|---|---|
npm install | pnpm install — or pnpm i |
npm install <pkg> | pnpm add <pkg> |
npm install -D <pkg> | pnpm add -D <pkg> |
npm uninstall <pkg> | pnpm remove <pkg> |
npm run build | pnpm build |
npx <pkg> | pnpm dlx <pkg> |
npm install <pkg> -w <name> | pnpm --filter <name> add <pkg> |
pnpm’s command surface is deliberately close to npm’s, so the translation is almost mechanical. The full table is above, with your package name filled in.
What actually changes
The commands are the easy part. The install layout is the reason to switch and the reason something breaks.
node_modules is not flat. npm and Yarn 1 hoist every transitive dependency to the top
level, which means code can require('some-package') that it never declared, and it works.
pnpm puts only your declared dependencies at the top and symlinks the rest from a
content-addressed store.
That is a real bug-finder, and it will find bugs in packages you did not write. A package that
relied on hoisting fails under pnpm with Cannot find module. The fix is usually
public-hoist-pattern in .npmrc, and sometimes filing an issue upstream.
Disk usage collapses. Every version of every package is stored once per machine and hard-linked into projects. Ten projects on the same React version cost one copy.
pnpm dlx is not npx with a different name. npx will happily run a binary already
present in the local node_modules; pnpm dlx always fetches into a temporary store and runs
that. Use pnpm exec for the local binary.
Migrating a project
- Delete
package-lock.jsonandnode_modules. - Run
pnpm install. - Run the build and the test suite. This is where the hoisting assumptions surface, not at install time.
- Commit
pnpm-lock.yaml, and onlypnpm-lock.yaml.
For a monorepo, add pnpm-workspace.yaml. pnpm does not read the workspaces field in
package.json — this is the single most common thing to get stuck on, because the install
succeeds and simply does not link anything.
Related
npm to Yarn, or the full four-way table including Bun.