npm — Yarn & pnpm
While npm is the default package manager, Yarn and pnpm offer compelling alternatives with different architectural philosophies. Each addresses specific shortcomings of npm — Yarn focuses on reliability and speed, pnpm on disk efficiency and strict dependency isolation.
Choosing the right package manager depends on your team's workflow, project size, monorepo requirements, and CI/CD constraints. This guide covers the strengths, trade-offs, and migration paths for each option.
Yarn Berry is a complete rewrite of Yarn Classic. Its defining feature is Plug'n'Play (PnP), which eliminates node_modules entirely by generating a single dependency map. This dramatically reduces install time and disk usage.
| 1 | # Install Yarn Berry globally |
| 2 | npm install -g yarn |
| 3 | |
| 4 | # Enable Berry in a project |
| 5 | yarn set version berry |
| 6 | |
| 7 | # Install with PnP |
| 8 | yarn install |
| 9 | |
| 10 | # Classic node_modules mode (if PnP is incompatible) |
| 11 | yarn config set nodeLinker node-modules |
| 12 | |
| 13 | # Add dependencies |
| 14 | yarn add express |
| 15 | yarn add -D typescript |
| Feature | Yarn Berry | Benefit |
|---|---|---|
| PnP | No node_modules | 10x faster installs, smaller repos |
| Zero-Install | Caches in repo | No install step in CI |
| Constraints | JS-based rules | Enforce workspace policies |
| SDKs | Editor support | TypeScript works without hoisting |
warning
pnpm uses a content-addressable store where every version of every package is stored exactly once on disk. node_modules uses hard links and symlinks, making it both disk-efficient and strictly isolated — packages cannot access dependencies they did not declare.
| 1 | # Install pnpm globally |
| 2 | npm install -g pnpm |
| 3 | |
| 4 | # Initialize project |
| 5 | pnpm init |
| 6 | |
| 7 | # Install dependencies |
| 8 | pnpm add express |
| 9 | pnpm add -D typescript |
| 10 | |
| 11 | # Deterministic install |
| 12 | pnpm install --frozen-lockfile |
| 13 | |
| 14 | # List store usage |
| 15 | pnpm store status |
| 16 | |
| 17 | # Prune unused store packages |
| 18 | pnpm store prune |
| Feature | pnpm | Benefit |
|---|---|---|
| Store | Global content-addressable | Single copy of each package version |
| Isolation | Strict node_modules | Prevents undeclared dependency access |
| Speed | Fastest installs | Up to 2x faster than npm |
| Disk | Minimal usage | Savings of 40-70% over npm |
pro tip
Real-world benchmarks show significant differences in install speed, disk usage, and CI pipeline performance across the three package managers.
| Metric | npm | Yarn Berry (PnP) | pnpm |
|---|---|---|---|
| Clean install | ~45s | ~8s | ~12s |
| Cached install | ~12s | ~1s | ~3s |
| Disk usage (100 deps) | ~350MB | ~120MB | ~80MB |
| Lockfile size | ~500KB | ~200KB | ~180KB |
Benchmarks from a typical Next.js project with ~1000 dependencies. Actual results vary by project size, network speed, and package composition. pnpm and Yarn Berry consistently outperform npm in both cold and warm cache scenarios.
info
Both Yarn and pnpm can import existing package-lock.json files and generate their own lockfile formats. Migration is straightforward for most projects.
| 1 | # Migrate from npm to Yarn Berry |
| 2 | npm install -g yarn |
| 3 | cd my-project |
| 4 | yarn set version berry |
| 5 | yarn install |
| 6 | |
| 7 | # Migrate from npm to pnpm |
| 8 | npm install -g pnpm |
| 9 | cd my-project |
| 10 | pnpm import # converts package-lock.json to pnpm-lock.yaml |
| 11 | pnpm install |
| 12 | |
| 13 | # Verify migration |
| 14 | pnpm ls --depth=0 |
Common Migration Issues
npm shrinkwrap files are not compatible. Delete them before migrating. Some tools like patch-packagerequire additional configuration with pnpm. ESLint plugins may need explicit dependency declarations with pnpm's strict resolver.
| 1 | # Enable hoisting for specific packages (pnpm) |
| 2 | echo 'hoist-pattern[]=*eslint*' >> .npmrc |
| 3 | |
| 4 | # Use pnpm overrides to replace dependencies |
| 5 | pnpm override lodash@^4.0.0 lodash@4.17.21 |
best practice
Zero-Install is a Yarn Berry feature where the package cache (compressed zip archives) is committed to the repository. Developers and CI environments can skip the yarn install step entirely, as dependencies are immediately available after git clone.
| 1 | # Enable zero-installs in .yarnrc.yml |
| 2 | echo 'enableGlobalCache: false' >> .yarnrc.yml |
| 3 | |
| 4 | # The .yarn/cache directory contains zip files |
| 5 | # .pnp.cjs is the dependency map |
| 6 | # Both are committed to git |
| 7 | |
| 8 | # No install step needed after clone |
| 9 | git clone my-project |
| 10 | cd my-project |
| 11 | node index.js # works immediately |
| 12 | |
| 13 | # Update dependencies |
| 14 | yarn add lodash |
| 15 | git add .yarn/cache/ .pnp.cjs |
| 16 | git commit -m "chore: add lodash" |
warning
All three package managers support workspaces for monorepo management, but their approaches differ significantly. pnpm and Yarn Berry offer more advanced features for large-scale monorepos.
| Feature | npm Workspaces | Yarn Workspaces | pnpm Workspaces |
|---|---|---|---|
| Hoisting | Flat | Controlled | Strict (isolated) |
| Filtering | Basic | Constraints | Recursive filtering |
| Deduplication | Automatic | Manual (yarn dedupe) | Automatic |
| Maturity | Stable | Mature | Mature |
| 1 | # pnpm workspace config (pnpm-workspace.yaml) |
| 2 | packages: |
| 3 | - "packages/*" |
| 4 | - "apps/*" |
| 5 | - "!packages/deprecated" |
| 1 | # pnpm: run command across packages |
| 2 | pnpm --filter @my/app build |
| 3 | pnpm --filter "./packages/**" test |
| 4 | |
| 5 | # Yarn: run across workspaces |
| 6 | yarn workspaces foreach run build |
| 7 | yarn workspace @my/app add lodash |
| 8 | |
| 9 | # npm: scoped workspace commands |
| 10 | npm run build -w @my/app |
| 11 | npm run test --workspaces --if-present |
best practice
Bun is a fourth option: a JavaScript runtime and package manager. bun install talks to the npm registry, writes bun.lock (text) or legacy bun.lockb (binary), and populates an npm-compatible node_modules tree — typically faster than npm, Yarn, and often pnpm on cold installs.
Unlike Yarn and pnpm, Bun is not managed by Corepack. Pin the Bun binary in CI (for example oven-sh/setup-bun) the same way you pin Node. Full guide: Bun as package manager.
| 1 | curl -fsSL https://bun.sh/install | bash |
| 2 | bun install |
| 3 | bun add express |
| 4 | bun add -d typescript |
| 5 | bun install --frozen-lockfile |
| 6 | bunx eslint . |
| 7 | bun run test |
| Bun strength | Caveat |
|---|---|
| Fastest installs in many benchmarks | Not via Corepack — team must pin versions |
| Runtime + test + bundler in one tool | Node API gaps; verify prod runtime |
| npm package compatibility is high | trustedDependencies for native postinstalls |
| Workspaces + bunx | Catalogs/constraints richer on pnpm/Yarn |
warning
Use this matrix to pick a default, then read the dedicated deep dive. The full four-way comparison (speed, disk, lockfiles, layouts) lives at npm vs Yarn vs pnpm vs Bun.
| If you need… | Choose | Deep dive |
|---|---|---|
| Zero extra tooling, tutorials, OSS default | npm | /docs/npm |
| PnP, Zero-Installs, constraints plugins | Yarn Berry | /docs/npm/yarn |
| Monorepos, disk savings, strict deps, catalogs | pnpm | /docs/npm/pnpm |
| Max install speed + optional unified runtime | Bun | /docs/npm/bun |
| Pin Yarn/pnpm versions for the whole team | Corepack | /docs/npm/corepack |
| 1 | # Typical company monorepo starter |
| 2 | corepack enable |
| 3 | npm pkg set packageManager=pnpm@9.15.0 |
| 4 | pnpm install |
| 5 | # Enforce: |
| 6 | # "preinstall": "npx only-allow pnpm" |
| 7 | |
| 8 | # Speed-first greenfield |
| 9 | bun install |
| 10 | # Pin bun-version in CI; document Node vs Bun for production |
| 11 | |
| 12 | # Yarn Zero-Install path |
| 13 | yarn set version stable |
| 14 | # enableGlobalCache: false — commit .yarn/cache |
best practice
| From → To | Key steps |
|---|---|
| npm → pnpm | pnpm import; rm package-lock.json; fix phantom deps |
| npm → Yarn Berry | yarn set version stable; optional nodeLinker node-modules |
| npm → Bun | bun install; set trustedDependencies for natives |
| Any → npm | Delete other locks; npm install; commit package-lock.json |
info
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.