npm vs Yarn vs pnpm (2026)
The JavaScript package manager war has settled into a three-way race. npm ships with Node.js and dominates by default. Yarn pioneered workspaces and offline caching. pnpm saves disk space and enforces strict dependency resolution.
But in 2026, which one should you actually use? We benchmarked all three on real-world projects using data from PkgPulse to find out.
The Current Landscape
npm remains the default — it ships with every Node.js installation. But "default" doesn't mean "best." Yarn (now in its Berry/v4 era) and pnpm have both eaten into npm's mindshare with genuine technical advantages.
| Metric | npm | Yarn (v4) | pnpm |
|---|---|---|---|
| Weekly downloads (CLI) | Ships with Node | 5.2M | 8.1M |
| GitHub stars | 8K | 41K | 30K |
| Default with Node.js | ✅ | ❌ | ❌ |
| Corepack support | ✅ | ✅ | ✅ |
pnpm's download numbers have grown 3x since 2024, making it the fastest-growing package manager in the ecosystem.
Install Speed Benchmarks
We tested clean installs (no cache) and cached installs on a real-world Next.js project with 847 dependencies:
Clean Install (No Cache)
| Package Manager | Time | vs npm |
|---|---|---|
| npm | 28.4s | baseline |
| Yarn (v4, node-modules) | 22.1s | 22% faster |
| Yarn (v4, PnP) | 14.3s | 50% faster |
| pnpm | 15.7s | 45% faster |
Cached Install
| Package Manager | Time | vs npm |
|---|---|---|
| npm | 12.1s | baseline |
| Yarn (v4, node-modules) | 8.3s | 31% faster |
| Yarn (v4, PnP) | 3.2s | 74% faster |
| pnpm | 5.8s | 52% faster |
Takeaway: Yarn with Plug'n'Play (PnP) is the fastest by a significant margin. pnpm is second. npm is consistently the slowest.
Disk Space Usage
pnpm's content-addressable storage is its killer feature. Instead of copying packages into every project's node_modules, pnpm hard-links them from a single store.
For a developer with 10 projects sharing common dependencies:
| Package Manager | Total Disk Usage | Savings vs npm |
|---|---|---|
| npm | 8.4 GB | baseline |
| Yarn (v4, PnP) | 2.1 GB | 75% less |
| pnpm | 1.8 GB | 79% less |
Takeaway: pnpm uses 79% less disk space than npm. If you work on multiple projects, the savings are massive.
Dependency Resolution & Security
Phantom Dependencies
npm and Yarn (with node-modules linker) allow "phantom dependencies" — packages your code can import even though they're not in your package.json. They just happen to be installed as transitive dependencies.
This is dangerous because:
- Your code works locally but fails in production if the transitive dependency changes
- You get no warning about undeclared dependencies
pnpm solves this by default. Its strict node_modules structure only exposes packages you explicitly depend on. Phantom imports throw errors immediately.
Lock File Security
All three use lock files, but the approaches differ:
- npm (
package-lock.json) — JSON format, includes resolved URLs and integrity hashes - Yarn (
yarn.lock) — Custom format, includes checksums - pnpm (
pnpm-lock.yaml) — YAML format, includes integrity hashes, most readable
Audit Commands
| Feature | npm | Yarn | pnpm |
|---|---|---|---|
audit command | ✅ | ✅ | ✅ |
| Auto-fix vulnerabilities | npm audit fix | Limited | pnpm audit --fix |
| Override vulnerable deps | overrides | resolutions | overrides or pnpm.overrides |
Monorepo Support
All three support workspaces, but the implementation quality varies:
npm Workspaces
Basic workspace support. Works, but limited tooling for task orchestration and filtering.
{
"workspaces": ["packages/*"]
}
Running commands across workspaces: npm run build --workspaces
Yarn Workspaces
The pioneer of JavaScript workspaces. Yarn v4 adds constraints (enforce rules across workspaces) and improved hoisting control.
{
"workspaces": ["packages/*"]
}
Running filtered commands: yarn workspaces foreach -A run build
pnpm Workspaces
The most mature workspace implementation. Built-in filtering, parallel execution, and --filter for targeting specific packages.
# pnpm-workspace.yaml
packages:
- 'packages/*'
Running filtered commands: pnpm --filter @myorg/ui build
Verdict: For monorepos, pnpm's filtering and strict isolation make it the strongest choice. If you need even more orchestration, pair it with Turborepo or Nx.
Plug'n'Play (PnP) — Yarn's Unique Feature
Yarn's PnP mode eliminates node_modules entirely. Instead, it generates a .pnp.cjs file that tells Node.js exactly where each package is on disk.
Pros:
- Fastest installs (no file copying)
- Zero phantom dependencies
- Smaller project footprint
- Deterministic resolution
Cons:
- Some packages don't support PnP (increasingly rare in 2026)
- Debugging can be harder (no
node_modulesto inspect) - IDE support requires plugins (VS Code extension available)
- Docker builds need adjustment
In 2026, PnP compatibility is much better than in 2022-2023. Most major packages work out of the box. But if you hit issues, Yarn lets you fall back to the node-modules linker.
Migration Paths
From npm to pnpm
# Install pnpm
corepack enable
corepack prepare pnpm@latest --activate
# Import existing lock file
pnpm import
# Install dependencies
pnpm install
# Update scripts (npm run → pnpm)
# In package.json, no changes needed — pnpm run works the same way
From npm to Yarn v4
# Enable Yarn via Corepack
corepack enable
corepack prepare yarn@stable --activate
# Initialize Yarn in your project
yarn set version stable
# Install dependencies (generates new lock file)
yarn install
Which Should You Choose?
Choose npm if:
- You want zero setup — it's already there
- Your team is small and install speed doesn't matter much
- You don't work on monorepos
- Simplicity is your top priority
Choose Yarn (v4) if:
- Maximum install speed matters (PnP mode)
- You want constraints for enforcing workspace rules
- Your team is comfortable with PnP's trade-offs
- You're already using Yarn and don't want to migrate
Choose pnpm if:
- Disk space is a concern (multiple projects)
- You want strict dependency resolution (no phantom deps)
- You're building a monorepo
- You want the best balance of speed, correctness, and DX
Our Recommendation
For most teams in 2026, pnpm is the best default choice. It's fast, saves disk space, prevents phantom dependency bugs, and has the best monorepo support. The ecosystem has fully embraced it — major frameworks like Next.js, Nuxt, and SvelteKit all work flawlessly with pnpm.
If you're starting a new project today, use pnpm. If you're on npm and things work fine, there's no urgent need to migrate — but the next time you start a project, give pnpm a try.
Compare all three package managers with real-time data on PkgPulse.
Corepack: Enforcing Package Manager Versions
In 2026, the recommended way to lock your project to a specific package manager is Corepack, now shipped with Node.js by default. It ensures everyone on your team — and your CI pipeline — uses the same package manager version:
// package.json — declare your package manager
{
"packageManager": "pnpm@9.15.4",
// or:
"packageManager": "yarn@4.6.0",
"packageManager": "npm@10.9.2"
}
# Enable Corepack (already enabled in Node.js 22+)
corepack enable
# When someone clones your repo and runs pnpm install:
# Corepack automatically installs the exact version from packageManager
# and uses that version — no need for manual pnpm install globally
Corepack's packageManager field also prevents accidentally using the wrong package manager. If your project uses pnpm and someone runs npm install, they get an error:
This project is configured to use pnpm.
Use "corepack pnpm" to run pnpm commands in this project.
This prevents mixed lock files — one of the most common CI "works on my machine" bugs.
CI/CD Configuration for Each Package Manager
Optimal CI setup differs by package manager:
npm (GitHub Actions)
- name: Install dependencies
run: npm ci # Always use ci, not install
# npm ci: deletes node_modules, installs from package-lock.json exactly
# npm install: may update package-lock.json (never do this in CI)
pnpm (GitHub Actions)
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Get pnpm store directory
id: pnpm-cache
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
Yarn (v4, GitHub Actions)
- uses: actions/setup-node@v4
with:
cache: "yarn"
- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: yarn install --immutable
# --immutable: equivalent to npm ci — fails if yarn.lock would change
The --frozen-lockfile (pnpm) / --immutable (yarn) / npm ci pattern is critical in CI: it ensures the installed packages exactly match the lock file and fails if they don't. This prevents silent lock file drift where CI installs different package versions than development.
2026 Ecosystem Update: Bun's Impact
Bun entered the package manager conversation seriously in 2024-2025. By 2026, Bun's package manager is fast enough (5-10x npm's speed) that many teams have switched for install performance alone — though they keep Node.js as the runtime.
The npm vs yarn vs pnpm comparison now has a fourth serious option. See our npm vs pnpm vs Yarn vs Bun article for the full four-way comparison including Bun.
For teams that can't use Bun (enterprise Node.js requirement, specific CI environments), pnpm remains the best choice among the three classic options. For new projects with flexibility, Bun's package manager is worth evaluating — its install speed advantage over pnpm is significant, especially in CI where cold install times matter.
Workspaces and Filtering Commands
For monorepo users, command-line filtering ergonomics matter day-to-day. Here is how each package manager handles targeted commands:
# Run build in a specific workspace package:
# npm
npm run build --workspace=packages/ui
npm run build -w packages/ui
# yarn v4
yarn workspace @myorg/ui build
yarn workspaces foreach --include '@myorg/ui' run build
# pnpm
pnpm --filter @myorg/ui build
pnpm --filter "./packages/ui" build
# Run in all packages matching a pattern:
pnpm --filter "@myorg/*" build
pnpm --filter "...[HEAD~1]" build # Only packages changed since last commit
# Run in dependents of a package:
pnpm --filter "...@myorg/ui" build # Build everything that depends on @myorg/ui
pnpm's --filter syntax is the most powerful — the ability to filter by changed files since a git ref ([HEAD~1]) and to filter by dependents (...@myorg/ui) makes it extremely capable for large monorepos. Turborepo and Nx work on top of these filtering primitives.
Lock File Comparison
Lock files are what make installs reproducible. Understanding their format helps when resolving merge conflicts:
# pnpm-lock.yaml (pnpm) — most readable
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
importers:
.:
dependencies:
react:
specifier: ^19.0.0
version: 19.0.0
packages:
react@19.0.0:
resolution: {integrity: sha512-...}
# yarn.lock (Yarn) — custom format
react@npm:^19.0.0:
version: 19.0.0
resolution: "react@npm:19.0.0"
checksum: sha512/...
languageName: node
linkType: hard
// package-lock.json (npm) — JSON, largest file size
{
"lockfileVersion": 3,
"packages": {
"node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-..."
}
}
}
pnpm's YAML format is the most human-readable and the easiest to resolve merge conflicts in. npm's JSON lock file is the largest on disk (often 3-5x larger than pnpm-lock.yaml for the same dependency set). Yarn's custom format is compact but requires familiarity to parse visually.
Migration Guide: npm to pnpm (Step by Step)
The most common migration in 2026 is npm to pnpm. Here is a complete guide:
# Step 1: Enable Corepack (ships with Node.js 16.9+)
corepack enable
# Step 2: Install pnpm via Corepack
corepack prepare pnpm@latest --activate
# Step 3: In your project — import the npm lock file
cd your-project
pnpm import
# Reads package-lock.json and generates pnpm-lock.yaml
# Step 4: Remove old lock file and node_modules
rm package-lock.json
rm -rf node_modules
# Step 5: Install with pnpm
pnpm install
# Step 6: Update package.json to declare package manager
# Add to package.json:
{
"packageManager": "pnpm@9.15.4"
}
# Step 7: Update CI — replace npm ci with pnpm install --frozen-lockfile
# See the CI/CD Configuration section above for full GitHub Actions setup
# Step 8: Commit
git add pnpm-lock.yaml package.json
git rm package-lock.json
git commit -m "chore: migrate from npm to pnpm"
Common issues during migration:
Phantom dependency errors: If your code imports packages not listed in package.json (but installed as transitive deps under npm), pnpm's strict mode will throw errors. Fix: add the package explicitly to dependencies or devDependencies.
Peer dependency warnings: pnpm is stricter about peer dependencies than npm. pnpm install will warn about unmet peers. Run pnpm install --strict-peer-dependencies=false temporarily to see what installs, then address warnings.
Scripts that reference npm run: pnpm runs npm scripts fine, but if your scripts explicitly call npm run something, you may need to update them to pnpm run something or use pnpm exec.
Methodology
Download data from npm registry (weekly average, February 2026). Benchmarks run on a 2024 MacBook Pro M3, macOS 14, Node.js 22 LTS, with a Next.js 15 project containing 847 dependencies. Results are averages over 5 runs.
Related: npm vs pnpm vs Yarn vs Bun Package Managers 2026, dependency management strategy for monorepos, npm supply chain security guide 2026.
See the live comparison
View npm vs. yarn on PkgPulse →