pnpm
pnpm
pnpm is one of the package managers in the Node.js ecosystem. This article covers pnpm's origins, how it works, and how it differs from other managers — based on facts.
1. About pnpm
Short for "performant npm", an open-source project started by Zoltan Kochan in 2017. The copyright reads "2015–present contributors of pnpm" — early experiments started in 2015 in the GitHub repo (pnpm/pnpm), reached 1.0, and continue today through 9.x and 10.x major versions. License is MIT.
The two problems it set out to solve:
- npm's behavior of installing the same package across many projects, filling up disk fast.
- The "phantom dependency" problem, where a flattened (hoisted)
node_moduleslets code import dependencies that were never declared.
2. How it works
pnpm's core is a content-addressable store. Package files are stored once in ~/.pnpm-store (or the OS default path), and each project's node_modules holds hard links pointing to those files. When many projects depend on the same library version, only one copy lives on disk.
The node_modules layout has two layers:
node_modules/.pnpm/<pkg>@<ver>/node_modules/<pkg>— the real package (hard-linked from the store).node_modules/<direct-dep>— a symlink to the path above.
Only directly declared dependencies are exposed at the top level, so code cannot require a module that isn't listed in package.json. The official docs call this "non-flat node_modules".
3. Workspaces
A pnpm-workspace.yaml at the root with package path patterns:
packages:
- "apps/*"
- "packages/*"
Internal dependencies are written with a protocol like "workspace:*" to point inside the workspace instead of the external npm registry.
pnpm-lock.yaml records exact versions and hashes for every dependency — a commit target.
4. Other paths
| Tool | First release | Trait |
|---|---|---|
| npm | 2010 | Bundled with Node.js. Flattened node_modules. Widest compatibility. |
| Yarn Classic (1.x) | 2016 | From Facebook. Introduced lockfile and workspaces ahead of npm. Now in maintenance mode. |
| Yarn Berry (2+) | 2020 | A PnP (Plug'n'Play) mode that drops node_modules entirely. Adoption splits over tooling compatibility. |
| pnpm | 2017 | Store + hard / symlinks. Disk savings and strict dependencies. |
| Bun | 2023 (1.0) | Zig-based runtime + package manager. bun install speed is its strength. |
The right fit depends on the environment. If CI assumes only standard Node tooling, npm; where disk savings and monorepo isolation matter, pnpm; for greenfield projects where speed is the priority, Bun.
5. Common shapes
# Install
# Windows: PowerShell
iwr https://get.pnpm.io/install.ps1 -useb | iex
# macOS · Linux: bash
curl -fsSL https://get.pnpm.io/install.sh | sh -
# In a project
pnpm install # install dependencies
pnpm add zod # add a runtime dependency
pnpm add -D vitest # dev dependency
pnpm remove zod
pnpm update
pnpm run build # or just pnpm build
Running a command for a specific package from the workspace root:
pnpm --filter web dev
pnpm -r build # run build in every workspace package
6. engines · packageManager
A common convention is to pin Node and pnpm versions in package.json:
{
"engines": {
"node": ">=20.10",
"pnpm": ">=9"
},
"packageManager": "pnpm@9.12.3"
}
Corepack (bundled with Node 16.10+) reads the packageManager field and prepares the matching manager version automatically.
7. Common pitfalls
phantom dependency — some tools assume a flattened node_modules and rely on phantom dependencies, which breaks under pnpm. Usually the fix is to declare the missing dependency in package.json. A workaround is shamefully-hoist=true in .npmrc, but it muddies intent.
Windows hard links — when the user profile sits on a different drive, hard links only work within the same volume. The store has to move to the same drive as the project. Use PNPM_HOME or pnpm config set store-dir.
Mixed lockfiles — when npm and pnpm lockfiles end up in the same repo, dependency resolution becomes shaky. Pair the packageManager field with Corepack so CI and local use the same manager.
pnpm dlx vs npx — different cache paths mean tools cached on one side may need to be re-fetched on the other.
Closing thoughts
pnpm's content-addressable store plus its non-hoisted layout deliver disk savings and phantom-dependency blocking together. The difference shows up the most in monorepos. Pin versions team-wide and CI-wide with the packageManager field plus Corepack.
Next
- monorepo-light
- python-uv
References include the official pnpm docs, pnpm motivation, pnpm GitHub, npm Documentation, Yarn Documentation, Node.js Corepack, and the pnpm blog.