npm, yarn, and pnpm are the three major JavaScript package managers. All three install packages from the npm registry, manage dependencies via package.json, and generate lockfiles for reproducible builds. They differ in performance, disk usage, security defaults, and monorepo support. Your choice affects developer experience and CI pipeline speed across every project.
| Feature | npm | Yarn (Berry) | pnpm |
|---|---|---|---|
| Install speed | Moderate | Fast (PnP mode) | Fastest (hard links) |
| Disk usage | High (duplicate copies) | Low (PnP) / High (node_modules) | Lowest (content-addressable store) |
| Lockfile | package-lock.json | yarn.lock | pnpm-lock.yaml |
| Monorepo support | Workspaces (basic) | Workspaces (advanced) | Workspaces + filtering (best) |
| Strictness | Flat node_modules (phantom deps) | Strict with PnP mode | Strict by default (symlinked) |
| Comes with Node.js | Yes (bundled) | No (via corepack) | No (via corepack or standalone) |
Why is pnpm faster and more efficient?
pnpm uses a global content-addressable store where each version of a package is saved exactly once on disk. Projects link to this store via hard links, so installing the same package across 10 projects uses the disk space of one copy. This also speeds up installs because packages already in the store don't need to be downloaded or extracted again. npm and Yarn Classic copy packages into each project's node_modules, wasting both time and disk space.
What are phantom dependencies and why do they matter?
npm and Yarn Classic flatten node_modules, which lets your code import packages that aren't in your package.json — these are "phantom dependencies." Your code works locally but breaks when the transitive dependency changes or disappears. pnpm prevents this by creating a strict node_modules layout where only declared dependencies are accessible. Yarn Berry's PnP mode also enforces strict dependency boundaries.
Which has the best monorepo support?
All three support workspaces, but pnpm offers the most mature monorepo experience with built-in filtering (--filter), recursive commands, and a dedicated pnpm-workspace.yaml config. Yarn Berry has strong workspace support with constraints and plugins. npm's workspace support is functional but lacks advanced filtering and has occasional hoisting issues. For large monorepos, pnpm or Yarn Berry is the standard choice.
How do lockfiles differ?
All three generate lockfiles for deterministic installs. npm uses JSON (package-lock.json), Yarn uses a custom format (yarn.lock), and pnpm uses YAML (pnpm-lock.yaml). pnpm's YAML lockfile is the most human-readable and produces the smallest diffs on updates. Yarn Berry's lockfile includes checksums for integrity verification. All three support --frozen-lockfile (or equivalent) for CI to ensure no unexpected changes.
When to use which?
Choose npm when you want zero setup friction (it ships with Node.js), your project is small, or your team prefers the most widely documented tool. npm is also the safest choice for open-source libraries since all contributors already have it.
Choose Yarn Berry when you want Plug'n'Play for zero-install repos, need constraint-based dependency management, or already use Yarn across your organization.
Choose pnpm when you need fast installs, efficient disk usage, strict dependency isolation, or excellent monorepo tooling. pnpm is the best all-around choice for new projects and monorepos.
Key takeaways
- pnpm is fastest and most disk-efficient thanks to its content-addressable store and hard linking
- pnpm and Yarn Berry enforce strict dependency isolation, preventing phantom dependency bugs
- npm ships with Node.js and has the lowest barrier to entry
- pnpm offers the best built-in monorepo support with filtering and recursive commands
- All three use the same package.json format and npm registry — switching is straightforward