Yarn & pnpm
npm is the default, but it is not the only package manager — and the alternatives exist for good reasons. Yarn pioneered lockfiles and workspaces; pnpm rethought node_modules entirely to save disk space and kill phantom dependencies. All three read package.json and talk to the same registry, so switching is mostly about the CLI and the node_modules layout.
The three managers
npm | Yarn | pnpm | |
|---|---|---|---|
Ships with Node | Yes | No | No |
Lockfile |
|
|
|
node_modules layout | Flat (hoisted) | Flat (hoisted) | Symlinked + content store |
Disk usage | High (copies per project) | High | Low (one global store) |
Phantom deps possible | Yes | Yes | No (strict by default) |
Best known for | Ubiquity | Workspaces, PnP | Speed + disk efficiency |
Command equivalents
The muscle memory transfers easily — the verbs are nearly identical:
Task | npm | Yarn | pnpm |
|---|---|---|---|
Install all |
|
|
|
Add a package |
|
|
|
Add dev dep |
|
|
|
Remove |
|
|
|
Run a script |
|
|
|
Clean install |
|
|
|
Run a binary once |
|
|
|
How pnpm changes node_modules
pnpm's headline feature is its store. Every version of every package is stored once on disk, globally, and projects link to it. This is why pnpm is dramatically more disk-efficient:
pnpm's content-addressable layout
~/.pnpm-store/ ← ONE global store, hard-linked into projects
└─ lodash@4.17.21/ ← stored once, shared by every project on the machine
project/node_modules/
├─ .pnpm/ ← the real packages, hard-linked from the store
└─ lodash → symlink into .pnpm/lodash@4.17.21
(only your DIRECT deps are symlinked at the top level)Yarn's two eras
"Yarn" means two quite different things, which trips people up:
Yarn Classic (1.x) | Yarn Modern (2+, "Berry") | |
|---|---|---|
node_modules | Yes (flat, like npm) | Optional — defaults to Plug'n'Play |
Install model | Conventional | PnP: a single |
Migration friction | Low | Higher — some tools assume node_modules exists |
Lock the manager with Corepack
Node ships Corepack, which pins which package manager (and version) a project uses, so teammates can't accidentally mix npm and pnpm in the same repo. Declare it with packageManager in package.json:
{ "packageManager": "pnpm@9.1.0" }corepack enable # let Corepack manage yarn/pnpm shims # now "pnpm install" uses exactly 9.1.0, fetched on demand
Which should you use?
npm — the safe default. Zero setup, universal docs, fine for most projects.
pnpm — choose for monorepos, many projects on one machine, or when strictness and disk savings matter. Increasingly the power-user default.
Yarn — choose Classic for legacy projects already on it; Modern/PnP only if you want its specific model and have verified tooling support.