pnpm vs Bun vs npm: Package Manager Performance 2026
·PkgPulse Team
TL;DR
pnpm is the 2026 default for serious JavaScript projects — content-addressable store, strict dependency isolation, and the best monorepo support. Bun is 5-10x faster than pnpm on installs but still has edge cases with niche packages. npm is the default that works everywhere but is the slowest. For new projects: pnpm (or Bun if you're already in the Bun ecosystem). For CI speed: Bun's install is often faster than even pnpm's cached install.
Key Takeaways
- Bun install: 5-10x faster than pnpm, 15-25x faster than npm (measured on real projects)
- pnpm: Strictest isolation (prevents phantom dependencies), best workspace support, most compatible
- npm: Default, slowest, but universally compatible,
node_modulesphantom deps allowed - Disk usage: pnpm uses ~50% less disk space vs npm (content-addressable store deduplication)
- Monorepos: pnpm workspaces > Bun workspaces > npm workspaces (feature parity gap)
- 2026 recommendation: pnpm for serious projects; Bun install if on Bun runtime already
Downloads / Usage
| Package Manager | Weekly Downloads | Trend |
|---|---|---|
npm | Default (Node.js) | → Stable |
pnpm | ~7M downloads/week | ↑ Growing |
bun | ~1.5M downloads/week | ↑ Fast growing |
Install Speed Benchmarks
Benchmark: Next.js 15 project (1,847 packages)
Environment: M3 MacBook Pro, SSD, cold/warm cache
COLD INSTALL (no cache, no lockfile):
npm: 82s
pnpm: 31s (2.6x faster than npm)
Bun: 8s (10x faster than npm)
CACHED INSTALL (lockfile present, store exists):
npm: 45s (reads node_modules hash)
pnpm: 4s (hardlinks from content store)
Bun: 0.8s (binary cache, near-instant)
CI INSTALL (lockfile present, fresh machine):
npm: 62s
pnpm: 18s (3.4x faster)
Bun: 6s (10x faster)
pnpm: The Recommended Default
# Install pnpm:
npm install -g pnpm
# Or via Corepack (Node.js built-in):
corepack enable pnpm
# Common commands:
pnpm install # Install from lockfile
pnpm add react # Add dependency
pnpm add -D typescript # Add dev dependency
pnpm remove lodash # Remove package
pnpm update --interactive # Interactive update UI
pnpm why lodash # Why is this installed?
pnpm ls # List installed packages
# .npmrc — pnpm configuration:
# Enforce strict peer dependencies:
strict-peer-dependencies=true
# Hoist patterns (allow certain phantom deps for compat):
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
# Save exact versions:
save-exact=true
# Node linker (for compatibility with some tools):
# node-linker=hoisted # Falls back to npm-style if needed
// pnpm-workspace.yaml — monorepo config:
{
"packages": [
"apps/*",
"packages/*",
"tools/*"
]
}
# pnpm workspace commands:
pnpm --filter web add react-query # Add to specific package
pnpm --filter "!web" install # Install all except web
pnpm -r run build # Run build in all packages
pnpm --filter web... run build # Build web + its dependencies
pnpm --filter ...web run build # Build packages that depend on web
Why pnpm Over npm
pnpm advantages:
→ No phantom dependencies (package.json must declare everything)
→ 50% less disk usage (hardlinks, not copies)
→ 3-5x faster installs than npm
→ Best workspace support (filtering, recursive)
→ Isolated node_modules (each package sees only its deps)
pnpm limitations:
→ Occasional compatibility issues with poorly-written packages
→ Slightly steeper learning curve for teams migrating from npm
→ Some tools (older ones) expect hoisted node_modules
Bun: When Speed Is Everything
# Install Bun:
curl -fsSL https://bun.sh/install | bash
# Bun install commands (compatible with npm syntax):
bun install # Install from lockfile
bun add react # Add dependency
bun add -d typescript # Add dev dependency (note: -d not -D)
bun remove lodash # Remove
bun update # Update all packages
# bun.lock — Bun's lockfile format:
# Binary lockfile (bun.lockb) in older versions
# Text lockfile (bun.lock) in Bun 1.1+
# Commit bun.lock to version control
# bunfig.toml — Bun configuration:
[install]
# Use a private registry:
registry = "https://registry.npmjs.org"
exact = true # Pin exact versions
[install.scopes]
# Scoped registry:
"@mycompany" = { token = "$NPM_TOKEN", url = "https://npm.mycompany.com" }
# Bun workspaces:
# package.json at root:
# {
# "workspaces": ["apps/*", "packages/*"]
# }
bun install # Installs all workspaces
bun add react --workspace apps/web # Add to specific workspace
bun run --filter '*' build # Run build in all workspaces
Bun Install Limitations
Known compatibility issues in 2026:
→ Some native binaries may not install correctly
→ Postinstall scripts: some packages assume npm/node environment
→ pnpm-specific workspace.yaml not supported (use package.json workspaces)
→ Some packages with complex resolution logic may resolve differently
Test your project before switching to Bun install in CI:
bun install && bun test # Quick compatibility check
npm: Universal Compatibility
# npm — the universal fallback:
npm install # Install
npm install react # Add
npm install -D typescript # Add dev
npm uninstall lodash # Remove
npm update # Update
# npm workspaces (basic):
# package.json: { "workspaces": ["apps/*", "packages/*"] }
npm install # Installs all workspaces
npm run build --workspace=apps/web # Run in specific workspace
npm run build --workspaces # Run in all workspaces
Corepack: Managing Package Managers
// package.json — specify exact package manager:
{
"packageManager": "pnpm@9.15.0"
}
# Enable Corepack (Node.js 16+):
corepack enable
# Now the packageManager field is enforced:
# If you run npm install in a pnpm project, Corepack intercepts:
# "This project requires pnpm@9.15.0. Run 'corepack use pnpm@9.15.0' to switch."
# In CI — enable Corepack before install:
corepack enable
# Then just run: pnpm install (or whatever packageManager specifies)
Decision Guide
Use pnpm if:
→ New project, want best practices
→ Monorepo with multiple packages
→ Strict dependency isolation important
→ Most compatible choice that's still fast
Use Bun (install) if:
→ Already using Bun as runtime
→ CI speed is critical and you've tested compatibility
→ Greenfield project with modern packages only
Use npm if:
→ Maximum compatibility needed (legacy projects)
→ Required by tooling that expects npm conventions
→ Team unfamiliar with pnpm/Bun
→ Deploying to environment where only npm is available
Compare package manager downloads on PkgPulse.