pnpm offers faster installs and better disk usage. Here's why you should consider switching.
Key Benefits#
1# Install pnpm
2npm install -g pnpm
3
4# Or with corepack
5corepack enable
6corepack prepare pnpm@latest --activate
7
8# Check version
9pnpm --versionContent-Addressable Storage#
Traditional npm:
project-a/node_modules/lodash/
project-b/node_modules/lodash/
project-c/node_modules/lodash/
# 3 copies of lodash
pnpm:
~/.pnpm-store/
lodash@4.17.21/ # Single copy
project-a/node_modules/.pnpm/lodash@4.17.21
project-b/node_modules/.pnpm/lodash@4.17.21
project-c/node_modules/.pnpm/lodash@4.17.21
# All symlinked to store
1# View store stats
2pnpm store status
3
4# Prune unused packages
5pnpm store prune
6
7# Check disk usage
8du -sh ~/.pnpm-storeStrict Node Modules#
1// npm allows accessing undeclared dependencies (hoisting)
2// This works with npm but shouldn't:
3import something from 'transitive-dep'; // Not in package.json
4
5// pnpm enforces strict mode
6// You can only import declared dependencies
7// This prevents phantom dependencies# If you need hoisting (legacy compatibility)
# .npmrc
node-linker=hoisted
shamefully-hoist=trueCommon Commands#
1# Install all dependencies
2pnpm install
3pnpm i
4
5# Add dependency
6pnpm add lodash
7pnpm add -D typescript # Dev dependency
8pnpm add -O webpack # Optional dependency
9pnpm add lodash@4.17.21 # Specific version
10
11# Remove dependency
12pnpm remove lodash
13pnpm rm lodash
14
15# Update dependencies
16pnpm update
17pnpm up lodash # Update specific
18pnpm up --latest # Update to latest (ignoring semver)
19
20# Run scripts
21pnpm run build
22pnpm build # Shorthand
23
24# Execute package binary
25pnpm exec tsc
26pnpm dlx create-react-app # Like npxLockfile#
1# pnpm-lock.yaml (more readable than package-lock.json)
2lockfileVersion: '9.0'
3
4settings:
5 autoInstallPeers: true
6
7importers:
8 .:
9 dependencies:
10 react:
11 specifier: ^18.2.0
12 version: 18.2.0
13
14packages:
15 react@18.2.0:
16 resolution: {integrity: sha512-...}
17 engines: {node: '>=0.10.0'}Workspaces (Monorepo)#
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
- '!**/test/**'1# Install all workspace dependencies
2pnpm install
3
4# Run command in specific package
5pnpm --filter @myorg/web build
6pnpm -F @myorg/web build
7
8# Run command in all packages
9pnpm -r build
10pnpm --recursive build
11
12# Run command in packages that changed
13pnpm -F '...[origin/main]' build
14
15# Add dependency to workspace package
16pnpm add lodash --filter @myorg/utils
17
18# Add workspace package as dependency
19pnpm add @myorg/utils --filter @myorg/web --workspace1// packages/web/package.json
2{
3 "name": "@myorg/web",
4 "dependencies": {
5 "@myorg/utils": "workspace:*"
6 }
7}Performance Comparison#
1# Benchmark (typical results)
2# Clean install:
3npm install # ~45 seconds
4yarn install # ~35 seconds
5pnpm install # ~15 seconds
6
7# With cache:
8npm install # ~20 seconds
9yarn install # ~10 seconds
10pnpm install # ~5 seconds
11
12# Disk usage (10 similar projects):
13npm # ~2.5 GB
14yarn # ~2.5 GB
15pnpm # ~500 MB (shared store)Configuration#
1# .npmrc
2# Strict peer dependencies
3auto-install-peers=true
4strict-peer-dependencies=true
5
6# Use specific store location
7store-dir=~/.pnpm-store
8
9# Prefer offline
10prefer-offline=true
11
12# Registry
13registry=https://registry.npmjs.org/
14
15# Workspace settings
16link-workspace-packages=true
17shared-workspace-lockfile=trueScripts and Hooks#
1// package.json
2{
3 "scripts": {
4 "preinstall": "npx only-allow pnpm",
5 "prepare": "husky install"
6 },
7 "packageManager": "pnpm@8.15.0"
8}# Run pre/post hooks
pnpm run prebuild # Runs before build
pnpm run build
pnpm run postbuild # Runs after buildOverrides and Patches#
1// package.json
2{
3 "pnpm": {
4 "overrides": {
5 "lodash": "4.17.21",
6 "foo@^1.0.0>bar": "2.0.0"
7 },
8 "patchedDependencies": {
9 "some-package@1.0.0": "patches/some-package.patch"
10 }
11 }
12}# Create patch
pnpm patch some-package@1.0.0
# Edit files in temporary directory
pnpm patch-commit <path>CI/CD Setup#
1# GitHub Actions
2name: CI
3
4on: [push, pull_request]
5
6jobs:
7 build:
8 runs-on: ubuntu-latest
9
10 steps:
11 - uses: actions/checkout@v4
12
13 - uses: pnpm/action-setup@v3
14 with:
15 version: 8
16
17 - uses: actions/setup-node@v4
18 with:
19 node-version: 20
20 cache: 'pnpm'
21
22 - run: pnpm install --frozen-lockfile
23 - run: pnpm build
24 - run: pnpm test1# Dockerfile
2FROM node:20-alpine
3
4RUN corepack enable
5
6WORKDIR /app
7
8COPY pnpm-lock.yaml package.json ./
9RUN pnpm install --frozen-lockfile --prod
10
11COPY . .
12RUN pnpm build
13
14CMD ["node", "dist/index.js"]Migration from npm/yarn#
1# Import from package-lock.json
2pnpm import
3
4# Remove npm/yarn files
5rm -rf node_modules package-lock.json yarn.lock
6
7# Install with pnpm
8pnpm install
9
10# Verify
11pnpm lsTroubleshooting#
1# Clear cache
2pnpm store prune
3
4# Rebuild node_modules
5rm -rf node_modules
6pnpm install
7
8# Check for issues
9pnpm why lodash # Why is package installed
10pnpm ls --depth 2 # List dependencies
11pnpm outdated # Check for updates
12
13# Verify store integrity
14pnpm store verifyBest Practices#
Project Setup:
✓ Add packageManager to package.json
✓ Use preinstall to enforce pnpm
✓ Commit pnpm-lock.yaml
✓ Configure .npmrc for team
Performance:
✓ Use --frozen-lockfile in CI
✓ Enable prefer-offline
✓ Prune store periodically
✓ Use workspace protocol
Monorepos:
✓ Define pnpm-workspace.yaml
✓ Use workspace: protocol
✓ Filter commands appropriately
✓ Share common configurations
Conclusion#
pnpm provides faster installs, better disk usage, and stricter dependency management. Its content-addressable store eliminates duplication, while strict mode prevents phantom dependencies. For monorepos, workspace support makes it an excellent choice for managing multiple packages.