Choosing the right package manager can have a significant effect on your project's performance and efficiency. The install command looks the same in all three, so the differences are easy to miss. They come down to how each one lays out node_modules.
The layout difference
npm and Yarn Classic hoist. Every package, including the dependencies of your dependencies, gets flattened to the root of the modules directory. pnpm does not. It uses symlinks to put only your direct dependencies at the root, and keeps everything else nested.
That sounds like an implementation detail until you hit the consequence. As pnpm's own documentation puts it, under hoisting "source code has access to dependencies that are not added as dependencies to the project." You can import a package you never installed, because something else pulled it in. It works locally and breaks when that transitive dependency changes version or disappears. These are phantom dependencies, and pnpm's layout makes them structurally impossible rather than merely discouraged.
One copy on disk instead of many
The second difference is where the files actually live. With npm, if you have 100 projects using a dependency, you have 100 copies of it on disk. pnpm keeps a single content-addressable store and hard-links files out of it, which as the docs say means they consume no additional disk space.
Updates are incremental at the file level too. If a new version of a 100-file package changes one file, pnpm update adds one file to the store rather than cloning the whole package again.
The install itself runs in three stages: resolve the dependency graph and fetch what is missing into the store, calculate the directory structure, then link everything into place.
Where Yarn Modern sits
Yarn Classic behaves like npm, hoisting into a flat tree. Yarn Modern took a different route with Plug'n'Play, which skips node_modules altogether and resolves packages through a lockfile-driven map. It solves the same strictness problem pnpm solves, by removing the directory rather than restructuring it.
Picking one
- npm is already installed with Node and needs no decision. Fine for a single project where disk space and strictness are not concerns.
- pnpm is the one to reach for with multiple projects or a monorepo. The disk savings are real and the strict layout catches a class of bug before it ships.
- Yarn is worth it if you specifically want Plug'n'Play, or you are on a codebase already invested in it.
The one genuine friction with pnpm is packages that assume a flat node_modules and reach for something they did not declare. That is the phantom dependency problem showing up from the other side, and the public-hoist-pattern setting exists to work around it when you cannot fix the package itself.





