npm copies every package into each project's node_modules and hoists them flat. pnpm keeps one content-addressable store on disk, hard-links files from it, and puts only your direct dependencies at the top of node_modules. That saves disk space and stops code from importing packages it never declared. Most commands map one to one.
| Topic | npm | pnpm |
|---|---|---|
| Where package files live | A copy inside each project | One shared store, hard-linked into projects |
| node_modules layout | Flat, everything hoisted | Direct dependencies only, the rest under node_modules/.pnpm |
| Lockfile | package-lock.json | pnpm-lock.yaml |
| Workspaces declared in | workspaces field in package.json | pnpm-workspace.yaml |
| Strict CI install | npm ci | pnpm install (frozen lockfile is the default in CI) |
| Run a package without installing | npx | pnpm dlx (also pnx, pnpx) |
Updated
Both tools read the same package.json and install from the same registry, so the choice is about layout, speed of repeat installs, monorepo tooling and strictness. Below is how each one stores packages, a translator for the commands you already know, and the steps to move an existing project from npm to pnpm.
| npm | pnpm |
|---|---|
npm install | pnpm install |
npm install <pkg> | pnpm add <pkg> |
npm install -D <pkg> | pnpm add -D <pkg> |
npm install -E <pkg> | pnpm add -E <pkg> |
npm install -g <pkg> | pnpm add -g <pkg> |
npm ci | pnpm install --frozen-lockfile |
npm uninstall <pkg> | pnpm remove <pkg> |
npm update | pnpm update |
npm install <pkg>@latest | pnpm update --latest <pkg> |
npm run dev | pnpm dev |
npm test | pnpm test |
npx <pkg> | pnpm dlx <pkg> |
npm exec <bin> | pnpm exec <bin> |
npm ls | pnpm list |
npm outdated | pnpm outdated |
npm audit | pnpm audit |
npm init -y | pnpm init |
npm run build -w app | pnpm --filter app build |
npm run build --workspaces | pnpm -r build |
pnpm import reads the npm lockfile so you keep the versions you already tested. Run your tests before committing.
# 1. install pnpm (standalone script from pnpm.io)
curl -fsSL https://get.pnpm.io/install.sh | sh -
# 2. convert the lockfile, then remove npm's files
pnpm import
rm -rf node_modules package-lock.json
# 3. install and test
pnpm install
pnpm testEvery folder matched here becomes a workspace package. Put the file at the repository root.
packages:
- "apps/*"
- "packages/*"workspace:* always resolves to the local copy of ui instead of a registry version.
{
"name": "web",
"dependencies": {
"ui": "workspace:*"
}
}
npm writes a full copy of each dependency into the project's node_modules and lifts nested dependencies to the top level where it can. Ten projects that use the same library hold ten copies.
pnpm downloads each file once into a global content-addressable store. Projects get hard links to those files, so a second project with the same versions takes almost no extra space, and a new version only adds the files that changed.
Inside the project, pnpm places symlinks for your direct dependencies in node_modules and keeps everything else under node_modules/.pnpm. A package can only reach what it declared.
npm workspaces are listed in the root package.json and run with -w name or --workspaces. pnpm lists them in pnpm-workspace.yaml and selects them with --filter, which also understands dependency graphs, for example a package plus everything it depends on.
pnpm's workspace: protocol pins a dependency to the local package and refuses to fall back to the registry. It is converted to a normal version range when you publish.
pnpm often reuses files already in its store, which helps repeat installs on the same machine. Speed depends on cache state, network and project size, so time both on your own project rather than relying on a published benchmark.
Yes. Run pnpm import to build a pnpm-lock.yaml from package-lock.json, npm-shrinkwrap.json or yarn.lock, then delete the old lockfile and node_modules and run pnpm install.
Your code, or a dependency, imports a package that is not listed in its own package.json. npm's flat layout hid that mistake. Add the missing package as a dependency of whatever imports it.
The pnpm installation docs describe pnpm 12 as a native executable that does not need Node.js to run itself. Your project scripts still need Node.js, of course.
npm, because it ships with Node.js and almost every tutorial uses it. Move to pnpm when you run a monorepo, many projects on one machine, or want the stricter node_modules layout.
Related: npm commands cheat sheet, Install Node.js and npm on Mac, Vite vs Next.js, Git cheat sheet.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.