pnpm vs npm vs Yarn vs Bun: Speed and Compatibility Compared

Four JavaScript package managers, one real decision: how pnpm, npm, Yarn and Bun compare on speed, disk space, monorepos and compatibility heading into 2026.

Official Yarn package manager branding image on a social preview card
Official Yarn branding image. Image: Yarn.

pnpm is the fastest of the four once its content-addressable store is warm, npm is the safest default because it ships with Node.js, Yarn Berry's Plug'n'Play model is the most radical departure from node_modules, and Bun's all-in-one installer is the quickest cold start — but which one is right for a given project in 2026 depends less on raw speed and more on disk usage, monorepo needs and how much ecosystem risk a team can tolerate.

Key verdict: For a single app with no special constraints, pnpm is the best general-purpose upgrade from npm — same registry, same lockfile-driven workflow, but a shared content-addressable store that avoids duplicate packages on disk. For large monorepos that want built-in task orchestration conventions, pnpm workspaces or Yarn Berry (with Plug'n'Play) are the strongest options. For teams already running Bun as their runtime, bun install is worth using purely for its cold-install speed, as long as the project doesn't depend on a Node.js API that Bun hasn't fully implemented yet. npm remains the right default when zero extra tooling and maximum compatibility matter more than speed.

At a Glance: pnpm vs npm vs Yarn vs Bun

Before the detail, here is how the four package managers compare on the dimensions that actually change a day-to-day workflow, based on each project's own documentation.

Package managerCurrent stable versionDisk modelDefault lockfileNative workspacesComes bundled with
npm12.2.0Per-project copies in node_modulespackage-lock.jsonYes (workspaces field)Node.js (every install)
pnpm12.14.0Single content-addressable store, hard-linkedpnpm-lock.yamlYes (pnpm-workspace.yaml)Nothing — installed separately
Yarn4.18.1 (Berry) / 1.22.22 (Classic)Plug'n'Play loader file by default, or node_modules opt-inyarn.lockYes (workspaces field)Nothing — installed via Corepack or standalone
Bun1.4.3Global cache, linked into node_modulesbun.lock (text)Yes (workspaces field + catalogs)The Bun runtime itself

How npm Works: The Default Everyone Starts With

npm ships inside every Node.js install, which is why it remains the baseline every other tool is compared against. Workspaces are declared with a workspaces array in the root package.json, and npm keeps a single package-lock.json at the repository root rather than one per package, per the official npm workspaces documentation. A workspace package that depends on a sibling workspace gets symlinked locally instead of being fetched from the registry. npm's architecture writes a full, non-deduplicated copy of each dependency tree into node_modules for every project, which is simple and maximally compatible with tools that expect a traditional node_modules layout, but it is also the reason npm comes out slowest in both pnpm's and Bun's own published benchmarks (more on those below).

How pnpm Saves Disk Space: the Content-Addressable Store

pnpm's core pitch is architectural, not just about speed. Its official Motivation page describes the problem directly: under npm or Yarn Classic, if 100 projects all depend on the same package, that package ends up physically copied onto disk 100 times. pnpm instead keeps one copy of every package version in a single global, content-addressable store, keyed by a hash of each file's contents, and then hard-links those files into each project's node_modules. Linking from the store consumes no additional disk space beyond the first copy, and when only one file in a package changes between versions, pnpm's store only has to add that one new file rather than duplicate the whole package.

That same non-flat node_modules structure — symlinks pointing into the store rather than a flattened tree — is also what gives pnpm its reputation for catching "phantom dependency" bugs: a package can only require() what it actually declared as a dependency, because nothing extra gets hoisted to the top level by default.

For a deeper walkthrough of why this matters for teams migrating off npm, see this explainer on what pnpm actually changes under the hood.

How Yarn Works: Classic vs. Berry and Plug'n'Play

"Yarn" means two different tools today. Yarn Classic (1.22.22) still uses a conventional, flat node_modules directory and is effectively in maintenance mode. Modern Yarn — known as Yarn Berry, currently at 4.18.1 — defaults to a completely different model called Plug'n'Play (PnP), documented on the official Yarn PnP page: instead of writing a node_modules folder at all, Yarn generates a single .pnp.cjs loader file that maps every import directly to its location inside Yarn's cache. That removes the filesystem-walk overhead Node.js normally pays to resolve a module, but it can also break tools and editors that assume a real node_modules tree exists — which is why Yarn Berry offers nodeLinker: node-modules as an escape hatch to opt back into the traditional layout while still getting Yarn's workspace tooling.

On monorepos specifically, Yarn's official workspaces documentation describes a fairly complete toolkit out of the box: a workspace: protocol for cross-referencing local packages (with ^, ~ and * shorthands that get rewritten to real semver ranges on publish), "constraints" for enforcing rules like synchronized dependency versions across packages, and yarn workspaces focus / yarn workspaces foreach for installing or running scripts against only the workspaces that matter for a given task.

How Bun's Installer Works: Speed and Lockfiles

Bun isn't just a package manager — it's a JavaScript runtime, bundler, test runner and package manager bundled into one binary, which is a different scope entirely from npm, pnpm or Yarn (Bun's runtime side is covered separately in this Bun vs. Deno vs. Node.js runtime comparison, which is about execution speed rather than package installation). For bun install specifically, Bun's official workspaces documentation states that it installed the Remix monorepo in roughly 500ms on Linux, and claims — in its own docs, not an independent test — to be about 28x faster than npm install, 12x faster than Yarn Classic's yarn install, and 8x faster than pnpm install on that workload. Bun also supports a catalog: protocol for declaring a dependency version once in the root package.json and referencing it from every workspace package.

Bun's lockfile has gone through a format change worth knowing about: as of v1.1.39, Bun switched its default to a human-readable text lockfile, bun.lock, replacing the earlier binary bun.lockb. Per Bun's own bun.lock announcement post, the text format also made repeat, cached installs around 30% faster than the binary format in the team's own benchmark. An existing bun.lockb or a package-lock.json can both be converted automatically on the first run of bun install --save-text-lockfile.

Install Speed: What the Published Benchmarks Actually Show

Every maintainer publishes numbers that flatter their own tool, so it's worth looking at exactly what each benchmark measured rather than quoting the multiplier alone.

pnpm runs a daily, automated benchmark suite comparing npm and successive pnpm releases and publishes the raw numbers on its official benchmarks page. In the "alotta-packages" scenario (roughly 1GB across fourteen toolchains), the run captured October 15, 2026 recorded a completely cold, fresh-clone install at 1 minute 37 seconds for npm 12.2.0 versus 13.1 seconds for pnpm 12.13.0 — and once everything was already warm and nothing had changed, npm needed 2.24 seconds to re-verify the tree while pnpm needed about 17 milliseconds. pnpm's own page is careful to note the numbers come from a single machine and will vary elsewhere, and should not be combined across its two different benchmark fixtures.

Bun's lockfile benchmark, published on its own blog, measured a different scenario — a cached, no-op install with node_modules already present — and found bun.lock completing in about 45.8ms, versus roughly 243ms for Yarn, 709.5ms for pnpm, and 1.525 seconds for npm on that author's machine. In a from-scratch install with no cached node_modules, the gap narrowed considerably across all tools but Bun still led.

The honest takeaway: pnpm and Bun both publish real, reproducible numbers showing large speed advantages over npm in cached and warm-start scenarios specifically — because both avoid npm's default of reconstructing the dependency tree from scratch even when nothing changed. Neither pnpm's nor Bun's own benchmarks were independently reproduced for this article, and no official Yarn benchmark page comparing installation speed against the other three was found, so Yarn's relative speed is not independently confirmed here beyond Bun's single-machine comparison.

Monorepo and Workspace Support Compared

All four tools now support multi-package repositories natively, but the configuration and guarantees differ:

npmpnpmYarn BerryBun
Workspace config filepackage.json "workspaces"pnpm-workspace.yamlpackage.json "workspaces"package.json "workspaces"
Local package protocolPlain semver + symlinkworkspace:workspace:workspace:
Shared version catalogsNot built inCatalogs (pnpm 9.5+)Via plugin/constraintsCatalogs (catalog:)
Task graph / cachingNoNo (installing/linking only)No (constraints/foreach only)No
Selective install of one workspacenpm install -wpnpm --filteryarn workspaces focusbun install --filter

None of the four package managers include a task-dependency graph or build cache — that's deliberately left to layered tools like Nx or Turborepo, which read whichever workspace config file is present. For teams already standardizing on Node.js itself, it's also worth checking which LTS line a workspace targets, since Node.js 26 becoming Active LTS later this month is the kind of runtime change that interacts with engine fields in every one of these package managers' manifests.

Compatibility and Ecosystem Risk

npm has the least compatibility risk by definition — it is the registry client every package is published and tested against. pnpm uses the same npm registry and the same package.json format, so compatibility issues are mostly limited to tools that hard-code assumptions about a flat node_modules tree, which pnpm's default non-flat structure can expose. Yarn Berry's Plug'n'Play mode is the biggest outlier: because there is no node_modules folder at all by default, any tool that reads the filesystem directly rather than going through Node's module resolution can break under PnP, which is exactly why Yarn ships the node-modules linker as a fallback.

Bun carries a different kind of risk because it is also a runtime, not only an installer. Bun's own Node.js APIs compatibility reference states that Bun is "steadily approaching full Node.js API compatibility" and that the team treats any package that works on Node.js but not on Bun as a Bun bug — but the same page lists several core modules, including node:child_process, node:crypto, node:tls and node:worker_threads, as only partially implemented, and a few APIs, such as node:sea and browser-style Storage, as not implemented at all. Using bun install purely to populate a node_modules folder that a separate Node.js runtime then executes avoids most of that risk; running the app itself on Bun does not.

Which to Choose in 2026

There isn't a universal winner, but the decision tree is reasonably simple:

  • Default / lowest-risk choice: npm, especially for small projects, published libraries that need to match what consumers run, or teams unwilling to add any extra tooling.
  • Best general upgrade from npm: pnpm, for the disk savings on machines with many Node.js projects and the stricter, phantom-dependency-resistant node_modules structure, without leaving the npm registry or lockfile-driven workflow behind.
  • Best for large monorepos that want strict dependency isolation: pnpm workspaces or Yarn Berry with Plug'n'Play — pnpm if the team wants to stay closest to a conventional node_modules layout, Yarn Berry if the team is comfortable with PnP's loader-file model and its tooling compatibility trade-offs.
  • Best if the team already runs Bun as its runtime: bun install, for the cold-start and cached-install speed Bun's own benchmarks document — as long as nothing in the stack depends on a Node.js API Bun hasn't fully implemented yet.

What to Do Next

Bottom line: speed numbers favor pnpm and Bun in cached, repeat-install scenarios by a wide margin over npm, according to each project's own published benchmarks, while npm and pnpm share the broadest compatibility because both target the same npm registry and a recognizable node_modules layout. Before switching an existing project, check three things against the official docs linked throughout this piece: whether the monorepo tooling already in use (Nx, Turborepo, or custom scripts) reads the workspace config file the new tool expects; whether any dependency or build step assumes a flat, fully-hoisted node_modules; and, if Bun is being considered as a runtime rather than just an installer, whether every Node.js API the project relies on is listed as fully implemented on Bun's compatibility reference. Running a trial migration in a branch with the new lockfile committed alongside the old one is the lowest-risk way to confirm compatibility before deleting the previous lockfile for good.

Frequently asked questions

Is pnpm actually faster than npm?

In pnpm's own daily benchmark suite (pnpm.io/benchmarks), pnpm is dramatically faster than npm in cached and warm-install scenarios because it only needs to verify or hard-link files already in its store rather than rewrite node_modules from scratch. In one run captured October 15, 2026, a warm, nothing-changed install took about 17ms with pnpm versus 2.24 seconds with npm. Cold, fresh-clone installs also favored pnpm but by a smaller margin.

What exactly is pnpm's content-addressable store?

It is a single, shared location on disk where pnpm keeps one copy of every package file, named by a hash of that file's content. Instead of copying a package into every project that needs it, pnpm hard-links the files from the store into each project's node_modules, so the same package version used by 100 projects still only takes up disk space once, per pnpm's official Motivation page.

Does using Bun's package manager mean giving up Node.js?

No. bun install can be used purely to populate node_modules from a package.json and lockfile, and the resulting dependencies can still be executed with Node.js. The compatibility risk Bun's own docs describe (partially or not-yet-implemented Node.js APIs) applies when the Bun runtime itself executes the application, not when bun install is used only as an installer.

Can pnpm or Yarn workspaces be adopted inside an existing npm monorepo?

Both can read an existing npm-style workspaces array in package.json. pnpm, as of the version documented on pnpm.io/workspaces, will even auto-generate a pnpm-workspace.yaml from that existing field if one doesn't already exist. Yarn Berry also reads a workspaces array natively. Teams should still expect to regenerate the lockfile and test for node_modules assumptions before fully switching.

Is Yarn Classic (1.x) still worth using in 2026?

Yarn's own release channel (repo.yarnpkg.com) lists 1.22.22 as the current Yarn Classic version, but active development has moved to Yarn Berry (4.18.1), which defaults to the Plug'n'Play model. New projects choosing Yarn should generally start on Berry rather than Classic.

Which package manager should a new JavaScript project pick in 2026?

For a standalone app or library, npm remains the lowest-risk default since it ships with Node.js and matches what most published packages are tested against. For teams that want the same registry and workflow but less disk usage, pnpm is the more common upgrade. Large monorepos often choose between pnpm workspaces and Yarn Berry depending on how much tooling compatibility with a traditional node_modules tree they need.

Sources

More on JavaScript Package Managers →pnpmnpmYarnBunJavaScriptMonorepos
Felix Moreau
Written byFelix Moreau

Felix Moreau writes Pandromeda's software coverage and how-to guides. He covers Windows, macOS and Linux updates, the apps people rely on, emulators and developer tools, and turns official documentation into clear, numbered steps that work on the current version.

More from Software & Guides

See all