Skip to main content
Back to blog

Why I switched from npm to pnpm

·3 min readDeveloper Tools

I used npm for years because it was the default and it worked. Then a project with 800+ dependencies took 90 seconds to install and I started looking at alternatives. pnpm cut that to 15 seconds and I never went back.

The speed difference

pnpm is fast because of how it stores packages. Instead of copying every package into each project's node_modules, pnpm keeps a single copy in a global store and creates hard links. If ten projects use lodash@4.17.21, it is stored once on disk.

The first install in a new project downloads packages normally. Subsequent installs (even in different projects) are nearly instant because pnpm links to packages already in the store.

Strict dependency resolution

This is the feature that matters more than speed in the long run. npm hoists packages to the top of node_modules, which means your code can accidentally import packages that are not in your package.json. It works on your machine because a transitive dependency happens to be hoisted, but breaks in production when the dependency tree is different.

pnpm does not hoist by default. If a package is not in your package.json, your code cannot import it. Period. This catches real bugs that npm hides.

# This might work with npm (accidentally using a transitive dependency)
# This fails with pnpm (as it should)
import something from "package-not-in-your-deps"

The switch

Switching from npm to pnpm was straightforward:

# Install pnpm
npm install -g pnpm
 
# Delete node_modules and lock file
rm -rf node_modules package-lock.json
 
# Install with pnpm
pnpm install

This generates a pnpm-lock.yaml and creates the symlinked node_modules structure. All my scripts, imports, and tooling worked without changes.

Workspace support

pnpm has excellent workspace support for monorepos. A single pnpm-workspace.yaml file at the root, and packages can reference each other with workspace:*. I wrote a detailed post about pnpm workspaces if you want the specifics.

Common commands

The CLI is almost identical to npm:

pnpm install          # Install dependencies
pnpm add lodash       # Add a dependency
pnpm add -D vitest    # Add a dev dependency
pnpm remove lodash    # Remove a dependency
pnpm run build        # Run a script
pnpm dlx create-next-app  # Run a package without installing (like npx)

The muscle memory transfer is immediate. pnpm instead of npm for most commands.

Compatibility

I have not encountered a project that does not work with pnpm. The occasional edge case with packages that rely on npm's hoisting behavior can be fixed by adding the package to pnpm.overrides or using the public-hoist-pattern setting.

Every CI/CD platform supports pnpm. GitHub Actions, GitLab CI, Cloudflare Pages, Railway. It is a first-class citizen in the JavaScript ecosystem now.

Why not yarn?

Yarn (especially Yarn Berry with PnP) takes a different approach that changes how node_modules works fundamentally. It is technically sound but the compatibility issues with some packages made it frustrating in practice. pnpm gives you similar speed benefits while keeping a traditional node_modules structure.

Sources

Enjoying the blog? Subscribe via RSS to get new posts in your reader.

Subscribe via RSS