What you'll learn
Quick Answer
npm ships with Node and is the safe default for most projects. pnpm is significantly faster and uses far less disk by hard-linking a shared store, and it is stricter about undeclared dependencies. Yarn's modern versions add advanced features but need more setup. The choice matters less than consistency — pick one per project, commit its lockfile, and never mix two in the same repository.
What They All Do
All three read package.json, resolve the dependency tree, download packages from a registry, and write a lockfile recording the exact versions installed.
The lockfile is the important part. package.json usually specifies ranges — ^4.18.0 means "4.18.0 or any compatible newer version". The lockfile records what was actually resolved, so a teammate or a server installs the identical tree rather than whatever is newest today.
npm → package-lock.json
yarn → yarn.lock
pnpm → pnpm-lock.yamlCommit the lockfile. Not committing it is a common mistake that produces builds differing between machines and mysterious failures that appear only in CI.
The commands map closely, which is why switching is easy:
npm install yarn pnpm install
npm install pkg yarn add pkg pnpm add pkg
npm install -D pkg yarn add -D pnpm add -D pkg
npm uninstall pkg yarn remove pnpm remove pkg
npm run build yarn build pnpm build
npm ci yarn --frozen-lockfile pnpm install --frozen-lockfile
Where They Genuinely Differ
Disk usage and the node_modules layout. This is the biggest practical difference.
npm and Yarn Classic copy every dependency into each project's node_modules. Ten projects using React means ten copies on disk. A single node_modules folder commonly reaches hundreds of megabytes.
pnpm stores each package version once in a global content-addressable store and creates hard links into each project. Ten projects using the same React version share one copy on disk. On a machine with many projects the saving is measured in gigabytes.
Strictness. npm and Yarn Classic produce a flat node_modules, which means you can require a package you never declared, simply because a dependency of yours installed it. That works until the day that transitive dependency changes and your code breaks for no visible reason.
pnpm uses a nested layout where only your declared dependencies are reachable. Undeclared imports fail immediately, which is stricter and correct — though it does surface pre-existing problems when migrating an old project.
Speed. pnpm is generally fastest, particularly on repeat installs where the store is already populated. npm has improved substantially and is no longer notably slow. Yarn Classic sits between them.
Which to Choose
Choose npm when: you are learning, working on a small project, or want zero setup. It ships with Node, every tutorial assumes it, and every platform supports it without configuration. For a fresher building projects, this is the right default and the differences will not affect you.
Choose pnpm when: you have many projects on one machine and disk space matters; you want the fastest installs; you are working in a monorepo; or you want strictness about declared dependencies. It is the technically strongest option and adoption has grown steadily.
Choose Yarn when: the project already uses it, or your team has settled on its modern workspace features. Yarn's newer versions changed significantly from Yarn 1, including an optional mode that removes node_modules entirely — powerful, but with real compatibility caveats worth understanding before adopting.
What actually matters more than the choice:
- Use one per project and commit its lockfile.
- Use the frozen-lockfile variant in CI —
npm cior the equivalent — so builds are reproducible and fail loudly when the lockfile disagrees withpackage.json. - Do not switch mid-project without deliberately removing the old lockfile and
node_modules.
Never Mix Them in One Project
This causes real, confusing problems and it happens easily — one developer runs npm install, another runs yarn, and now the repository has two lockfiles.
package-lock.json ← npm's view of the tree
yarn.lock ← Yarn's view, resolved separatelyThey can resolve different versions of the same transitive dependency. So the application works for one developer and fails for another, and CI may install a third combination depending on which command it runs. Debugging this is genuinely painful, because the code is identical and only the installed tree differs.
If it has already happened: decide on one, delete the other lockfile and node_modules, reinstall cleanly, and commit the result.
rm -rf node_modules yarn.lock
npm install
git add package-lock.json && git rm --cached yarn.lockPrevent it by declaring the intended manager in package.json, which modern Node can enforce:
{
"packageManager": "pnpm@9.0.0"
}Add the other lockfiles to .gitignore as a second line of defence, and mention the expected command in your README.
Practical Notes Worth Knowing
npm ci is not the same as npm install. It deletes node_modules and installs exactly what the lockfile specifies, failing if the lockfile and package.json disagree. It is faster and reproducible, which is why it is the correct command in CI and deployment. npm install may resolve new versions and modify the lockfile — fine locally, wrong in automation.
Dependencies versus devDependencies. Runtime code goes in dependencies; build tools, linters and test frameworks go in devDependencies. Production installs commonly skip devDependencies, which is why a misplaced package produces "cannot find module" in production while working locally.
Use npx for one-off tools rather than installing them globally. It downloads, runs and discards, which avoids version conflicts and permission problems:
npx create-react-app my-appAvoid sudo npm install -g. It creates root-owned files that cause permission errors afterwards, which people then fix with more sudo. Use a Node version manager so everything lives in your home directory.
Audit occasionally — npm audit reports known vulnerabilities. Read before running --force, since it can install breaking major versions to resolve an advisory that may not affect how you use the package.
