Package managers determine development workflow. npm ships with Node.js, Yarn offers speed improvements, and pnpm saves disk space. At ZIRA Software, we evaluate package managers per project for optimal performance.
Overview
npm (Node Package Manager):
- Default with Node.js
- Largest registry (npmjs.com)
- Stable and widely adopted
- Improved significantly since v5
Yarn:
- Created by Facebook (2016)
- Faster installs (v1)
- Workspaces support
- Deterministic installs
pnpm (Performant npm):
- Most disk-efficient
- Fast installations
- Strict node_modules structure
- Hard links instead of copies
Installation Speed
Benchmark (1000 packages):
# Cold cache
npm install: ~45 seconds
yarn install: ~35 seconds
pnpm install: ~28 seconds
# With cache
npm install: ~20 seconds
yarn install: ~12 seconds
pnpm install: ~8 seconds
Why pnpm is fastest:
- Reuses packages via hard links
- No duplicate installations
- Parallel operations
Disk Space Usage
Same project installed 10 times:
npm: ~1.5 GB (copies for each project)
Yarn: ~1.3 GB (some optimization)
pnpm: ~300 MB (single store, hard links)
pnpm advantage:
# Global store
~/.pnpm-store/
v3/
files/
00/
# Packages stored once
Every project links to same package files.
Lock Files
npm (package-lock.json):
{
"name": "my-app",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"node_modules/react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-..."
}
}
}
Yarn (yarn.lock):
react@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz"
integrity sha512-...
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
pnpm (pnpm-lock.yaml):
lockfileVersion: 5.3
specifiers:
react: ^16.13.1
dependencies:
react: 16.13.1
packages:
/react/16.13.1:
resolution: {integrity: sha512-...}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
Basic Commands
Installing packages:
# npm
npm install
npm install react
npm install --save-dev webpack
# Yarn
yarn
yarn add react
yarn add --dev webpack
# pnpm
pnpm install
pnpm add react
pnpm add --save-dev webpack
Removing packages:
# npm
npm uninstall react
# Yarn
yarn remove react
# pnpm
pnpm remove react
Running scripts:
# npm
npm run build
npm test
# Yarn
yarn build
yarn test
# pnpm
pnpm run build
pnpm test
Workspaces (Monorepos)
npm workspaces (v7+):
{
"name": "monorepo",
"private": true,
"workspaces": [
"packages/*"
]
}
npm install -w packages/app
npm run test --workspaces
Yarn workspaces:
{
"private": true,
"workspaces": [
"packages/*"
]
}
yarn workspace app add react
yarn workspaces run test
pnpm workspaces:
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
pnpm --filter app add react
pnpm -r test # Run in all workspaces
Security
npm audit:
npm audit
npm audit fix
npm audit fix --force
Yarn audit:
yarn audit
# No auto-fix in v1
pnpm audit:
pnpm audit
pnpm audit --fix
Node Modules Structure
npm/Yarn (flat structure):
node_modules/
react/
react-dom/
lodash/
# All packages at top level (hoisting)
pnpm (nested structure):
node_modules/
.pnpm/
react@16.13.1/
node_modules/
react/
loose-envify/
react -> .pnpm/react@16.13.1/node_modules/react
pnpm prevents phantom dependencies:
// This works in npm/Yarn but shouldn't
import _ from 'lodash'; // Not in package.json!
// pnpm blocks this - must declare all dependencies
CI/CD Optimization
npm:
# .github/workflows/ci.yml
- name: Install dependencies
run: npm ci # Uses package-lock.json, faster than install
Yarn:
- name: Get yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies
run: yarn install --frozen-lockfile
pnpm:
- uses: pnpm/action-setup@v2
with:
version: 7
- name: Get pnpm store
id: pnpm-cache
run: echo "::set-output name=dir::$(pnpm store path)"
- uses: actions/cache@v2
with:
path: ${{ steps.pnpm-cache.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
Migration
npm to Yarn:
# Remove npm files
rm -rf node_modules package-lock.json
# Install with Yarn
yarn install
# Commit yarn.lock
git add yarn.lock
npm/Yarn to pnpm:
# Install pnpm
npm install -g pnpm
# Remove old files
rm -rf node_modules package-lock.json yarn.lock
# Import and install
pnpm import # Converts lock file
pnpm install
# Commit pnpm-lock.yaml
git add pnpm-lock.yaml
When to Use Each
Use npm when:
- Simple projects
- Need maximum compatibility
- Team familiar with npm
- CI/CD already configured
Use Yarn when:
- Need workspaces (better than npm v6)
- Want faster installs than npm
- Large monorepo projects
- Yarn 2/3 features needed
Use pnpm when:
- Disk space matters
- Multiple projects on machine
- Want strict dependency management
- Need fastest installs
- Monorepo with many shared dependencies
Real-World Recommendations
Small projects (less than 50 dependencies):
- Any package manager works fine
- Use what team knows
Large applications (100+ dependencies):
- pnpm for disk savings
- Yarn for speed + workspaces
Monorepos:
- pnpm (best performance + disk usage)
- Yarn (mature workspace support)
CI/CD focus:
- npm ci (fastest with proper caching)
- pnpm (smallest cache size)
Performance Tips
1. Use exact lock file install:
npm ci # npm
yarn install --frozen-lockfile # Yarn
pnpm install --frozen-lockfile # pnpm
2. Cache in CI:
- Cache node_modules or package manager store
- Use lock file hash as cache key
3. Prune dev dependencies:
npm prune --production
yarn install --production
pnpm install --prod
Conclusion
pnpm offers best disk efficiency and speed. Yarn provides excellent workspace support. npm remains reliable default. Choose based on project needs and team familiarity.
Need help optimizing your JavaScript build process? Contact ZIRA Software for frontend development consultation.