npm vs pnpm vs yarn: Comparison and Command Cheatsheet
TL;DR
npm decides dependency versions by checking npm-shrinkwrap.json, then package-lock.json, then yarn.lock. pnpm stores dependencies in a content-addressable store shared across projects, adding only files that differ. yarn installs exact versions from yarn.lock when it satisfies package.json and does not check for newer versions. All three use install as the entry command, but saving dependencies differs: npm uses flags like -D, while yarn uses yarn add --dev. For CI, yarn recommends --frozen-lockfile.
Core Comparison Table
| Item or point | Value or description | Caveat |
|---|---|---|
| npm lockfile precedence | npm-shrinkwrap.json > package-lock.json > yarn.lock | Installation is driven in this order |
| npm default save location | dependencies | -D saves to devDependencies, --save-peer to peerDependencies, -O to optionalDependencies |
| npm exact version | -E or --save-exact | Default uses npm's semver range operator |
| npm production install | --production or NODE_ENV=production | Skips devDependencies; --production=false forces full install |
| npm alias install | <alias>@npm:<name> | Applies to your project only, does not rename transitive deps |
| pnpm storage | Content-addressable store, hard links or copy-on-write | Copies by default when the filesystem supports neither |
| pnpm node_modules | Non-flat by default, symlinks only direct deps | Set nodeLinker=hoisted if tooling dislikes symlinks |
| pnpm install stages | Resolution, directory structure calculation, linking | Each package advances as soon as ready, no waiting |
| yarn with no command | Running yarn equals yarn install | Passes through provided flags |
| yarn lockfile behavior | Installs locked versions when yarn.lock satisfies package.json | Does not check for newer versions |
| yarn frozen install | --frozen-lockfile | Does not generate a lockfile and fails if an update is needed |
| yarn single version | --flat | Prompts once to pick one version for multi-range deps, written to resolutions |
| yarn audit | --audit | Unlike npm, yarn audits only when requested |
Install and Dependency Management Details
npm: lockfile precedence and save flags
Running npm install with no arguments in a package directory installs the modules listed as dependencies in package.json into the local node_modules. Added packages are saved to dependencies by default; use -D, --save-peer, -O, or --no-save to change the location, and -E to save an exact version. When installing a folder inside the project root, its dependencies are installed and may be hoisted; a folder outside the root gets a symlink instead, and --install-links installs its contents like a registry package.
pnpm: disk usage and install speed
pnpm stores dependencies in a content-addressable store: if 100 projects use one dependency, there are no longer 100 copies on disk; if a new version changes 1 of 100 files, pnpm update adds only 1 new file to the store. Installation runs in three stages—dependency resolution, directory structure calculation, and linking—and each package moves on as soon as it is ready. node_modules is non-flat by default, with only direct dependencies symlinked at the root.
yarn: reproducible installs and common flags
yarn replaces npm's --save and --save-dev with yarn add and yarn add --dev. For reproducible dependencies in CI, pass --frozen-lockfile; --check-files verifies installed files in node_modules were not removed; --force refetches all packages; --ignore-scripts skips scripts defined in package.json and its dependencies; --modules-folder <path> sets an alternate node_modules location.
# npm: install and save as a dev dependency with an exact version
npm install node-tap --save-dev --save-exact
# npm: install two versions of the same package under aliases
npm install jquery2@npm:jquery@2 jquery3@npm:jquery@3
# pnpm: install all dependencies (non-flat node_modules by default)
pnpm install
# pnpm: switch to a flat structure if tooling dislikes symlinks
pnpm install --config.nodeLinker=hoisted
# yarn: reproducible install in CI
yarn install --frozen-lockfile
# yarn: force a single version for multi-range dependencies
yarn install --flatFAQ
Does npm install read yarn.lock?
Yes, but with the lowest precedence. npm decides installation by checking npm-shrinkwrap.json, then package-lock.json, then yarn.lock.
Why does pnpm save disk space?
Because dependencies live in a content-addressable store, and installs hard-link or copy-on-write files from that single location, so the same version is shared across projects.
When does yarn install update yarn.lock?
When yarn.lock is absent or does not satisfy the dependencies listed in package.json, yarn looks for the newest versions that satisfy the constraints and writes them to yarn.lock.
How do I make yarn fail when the lockfile needs an update?
Use yarn install --frozen-lockfile, which does not generate yarn.lock and fails if an update is needed.
How does pnpm's node_modules differ from npm's?
npm and Yarn Classic hoist packages to the root of the modules directory; pnpm by default symlinks only direct dependencies at the root, and you can set nodeLinker to hoisted for a flat layout.
Sources
- https://docs.npmjs.com/cli/v10/commands/npm-install (accessed 2026-11-06)
- https://pnpm.io/motivation (accessed 2026-11-06)
- https://classic.yarnpkg.com/lang/en/docs/cli/install/ (accessed 2026-11-06)