npm, pnpm, and Yarn: A Practical Guide to JavaScript Package Managers

Modern JavaScript development is unthinkable without package managers. React, TypeScript, ESLint, Webpack, Vite, Jest, Cypress — almost everything you use comes from npm packages.
But there isn’t just one way to manage them.
Today, the three most common tools are:
- npm — the default, ships with Node.js
- Yarn — introduced faster installs and better workflows
- pnpm — focuses on speed and disk efficiency with a clever storage model
In this post, we’ll walk through:
- What a package manager actually does
- The core concepts that npm, pnpm, and Yarn share
- How each tool behaves, with concrete examples
- Strengths and trade-offs of each
- How to pick one for your projects and monorepos
1. What Does a Package Manager Do?
At a high level, a package manager:
- Installs dependencies listed in your
package.json - Resolves and pins exact versions in a lockfile
- Creates and manages the
node_modulesfolder - Runs scripts defined in
package.json(lint, test, build, etc.) - Publishes packages to registries like npmjs.com
A minimal package.json might look like:
{
"name": "my-app",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest"
},
"dependencies": {
"react": "^18.3.0",
"react-dom": "^18.3.0"
},
"devDependencies": {
"vite": "^5.0.0",
"vitest": "^2.0.0"
}
}All three package managers understand this file. The differences lie in:
- Install speed
- Disk space usage
- Lockfile format
- Workspace/monorepo support
- Extra features and workflows
2. Core Concepts: Same Ideas, Different Implementations
Before we dive into each tool, let’s align on concepts they all share.
2.1 Dependencies and Version Ranges
In package.json:
"dependencies": {
"axios": "^1.7.0",
"zustand": "~5.0.0"
}Common version range operators:
-
^1.7.0- Accepts
>=1.7.0 <2.0.0 - Allows new features and bug fixes, but blocks breaking changes
- Accepts
-
~5.0.0- Accepts
>=5.0.0 <5.1.0 - Only patch updates; more conservative
- Accepts
Exact versions:
"typescript": "5.6.3"pin a specific version. The lockfile is what makes installs reproducible across machines and CI.
2.2 Lockfiles
Each tool has its own lockfile format:
- npm:
package-lock.json - Yarn (Classic):
yarn.lock - Yarn Berry (v2+): still
yarn.lock, but different format - pnpm:
pnpm-lock.yaml
Lockfiles store exact versions and integrity hashes so that:
- Local dev, CI pipelines, and production builds all resolve to the same versions
- Installs become faster, since resolution work is cached
Rule of thumb:
Commit your lockfile to Git for apps. For libraries, lockfile strategy can vary, but you almost always commit it for the primary repo.
2.3 Scripts
All of them support npm run style scripts:
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "vitest run",
"lint": "eslint src --ext .ts,.tsx"
}Commands:
- npm:
npm run dev - pnpm:
pnpm dev(shorthand forpnpm run dev) - Yarn:
yarn dev
They all inject node_modules/.bin into the PATH, so you can run tools without global installs.
3. npm: The Default Package Manager
3.1 What Is npm?
npm is:
- The default package manager installed with Node.js
- The name of the client (
npmCLI) - Also the name of the registry (
https://registry.npmjs.org)
If you install Node, you get npm automatically:
node -v
npm -v3.2 Basic npm Workflow
Initialize a project:
npm init -yInstall dependencies:
# Add a regular dependency
npm install react react-dom
# Add a dev dependency
npm install --save-dev typescript viteThis will:
- Update
dependenciesordevDependenciesinpackage.json - Create or update
package-lock.json - Populate
node_modules/with installed packages
Run scripts:
npm run dev
npm run build
npm testUse npx (or npm exec) for one-off commands:
npx create-next-app my-app
npx vitest3.3 Pros of npm
- Comes with Node; zero extra setup
- The default ecosystem target (everything is tested with npm)
- Actively improved; performance and features have gotten much better in recent versions
- npm workspaces are now a built-in way to manage monorepos
Example of npm workspaces:
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"]
}Then:
npm install
npm run build --workspace apps/web3.4 Cons / Limitations
Historically (especially older versions):
- Slower installs compared to pnpm and Yarn
node_moduleslayout can be very large and deeply nested- Earlier versions had more issues with deterministic installs
Many of these have improved, but npm is still not the most disk-efficient option.
4. Yarn: Faster Installs and Better Workflows
4.1 Why Yarn Was Created
Yarn was originally created by Facebook (and others) to address issues they saw with npm at the time:
- Slow and sometimes unreliable installs
- Non-deterministic dependency resolution
- Problems working on very large codebases
Yarn introduced:
- A different lockfile format (
yarn.lock) - Parallelized downloads and caching for speed
- A focus on deterministic installs
4.2 Yarn Classic vs Yarn Berry
There are two “families” of Yarn:
-
Yarn Classic (v1)
- Very popular, especially in older projects
- Uses
node_moduleslike npm - Great support for workspaces
-
Yarn Berry (v2+)
- Completely redesigned
- Supports Plug’n’Play (PnP) — no
node_modulesat all - More opinionated and config-heavy
- Very powerful, but can surprise newcomers
This post will mostly treat them together at a high level and mention differences where relevant.
4.3 Basic Yarn Workflow
Initialize:
yarn init -yInstall dependencies:
# Add a regular dependency
yarn add react react-dom
# Add a dev dependency
yarn add --dev typescript viteRun scripts:
yarn dev
yarn build
yarn testInstall all dependencies (after cloning a repo):
yarn install4.4 Yarn Workspaces
Yarn popularized the concept of workspaces for monorepos.
In your root package.json:
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"]
}Now:
apps/webcan depend on@my-scope/uifrompackages/ui- Yarn links them locally (no publishing required)
yarn installdeduplicates dependencies across all workspaces
Workspaces are extremely useful for:
- Shared components (
packages/ui) - Utility libraries (
packages/utils) - Config packages (
packages/eslint-config,packages/tsconfig)
4.5 Plug’n’Play (Yarn Berry Feature)
Yarn Berry introduced Plug’n’Play (PnP):
- No
node_modulesfolder - Yarn creates a single zip archive for dependencies and a
.pnp.cjsfile - Modules are resolved via the PnP API instead of filesystem crawling
Pros:
- Faster installs and smaller disk usage
- Catches non-declared dependencies (you must declare them)
Cons:
- Some tools assume
node_modulesexists and need adapters - Slightly steeper learning curve
Many teams stay on Yarn Classic or use Berry without PnP if they prefer a smoother transition.
5. pnpm: Performance and Disk Efficiency
5.1 What Makes pnpm Different?
pnpm was created to solve two big problems:
- Deeply nested
node_modulesfolders wasting a lot of disk space - Slow, repeated downloads of the same packages across projects
Its core idea:
Use a content-addressable store and symlinks instead of duplicating packages in each project.
In plain English:
- Dependencies are stored once in a global store (per machine)
- Each project’s
node_modulesfolder points to that store via symlinks - You can have dozens of projects using React, but React’s files only live on disk once
5.2 Basic pnpm Workflow
Install pnpm (one-time):
npm install -g pnpmInitialize:
pnpm initInstall dependencies:
# Regular dependency
pnpm add react react-dom
# Dev dependency
pnpm add -D typescript viteRun scripts:
pnpm dev
pnpm build
pnpm testInstall all dependencies:
pnpm install5.3 pnpm’s Node Modules Layout
pnpm creates a special node_modules structure:
- Each package is a symlink to the global store
- The actual files live in something like
~/.pnpm-store - Node’s module resolution still works, but it enforces stricter rules
Benefits:
- Massive disk space savings, especially in monorepos
- Faster installs once packages are cached
- Helps catch bad patterns like accessing undeclared dependencies
5.4 pnpm Workspaces
pnpm has first-class support for workspaces via pnpm-workspace.yaml:
packages:
- 'apps/*'
- 'packages/*'Then:
pnpm install
pnpm -r test # run test in all packages (recursive)
pnpm -r buildThe -r (recursive) flag is extremely handy in monorepos.
5.5 Why Many Monorepos Prefer pnpm
You’ll often see pnpm used in larger monorepos because:
- It uses far less disk space
- Workspace support is solid and simple
- It enforces better dependency hygiene
- It keeps install times very competitive
For a big repo with many packages and branches, these benefits add up quickly.
6. Comparing npm, pnpm, and Yarn
Here’s a simplified overview:
| Feature / Aspect | npm | Yarn | pnpm |
|---|---|---|---|
| Default with Node.js | ✅ Yes | ❌ No | ❌ No |
| Lockfile | package-lock.json | yarn.lock | pnpm-lock.yaml |
| Disk usage | High | Medium (Classic), Low (PnP) | Low (shared store + symlinks) |
| Speed (cold install) | Good (recent versions) | Good | Very good |
| Speed (warm install) | Good | Very good | Very good |
| Workspaces support | ✅ Yes (built-in) | ✅ Yes | ✅ Yes |
| Global store | Basic cache | Cache | Content-addressable store |
| Node.js ecosystem fit | Excellent (default assumption) | Excellent | Excellent |
| PnP (no node_modules) | ❌ No | ✅ Yarn Berry | ❌ No |
| Learning curve | Easiest | Moderate (Berry more complex) | Moderate (due to store and layout) |
All three are good choices in 2025. The question is more about fit than “which one is objectively best.”
7. Choosing the Right Package Manager
Here are some practical guidelines.
7.1 When npm Is a Great Choice
Use npm if:
- You prefer simplicity and minimal tooling decisions
- You’re working on small to medium apps
- Your team is new to Node/JS tooling
- You want to avoid explaining extra global tools
Recent npm versions support:
- Workspaces
- Lockfiles
- Good performance
For many apps, npm is more than enough.
7.2 When Yarn Makes Sense
Use Yarn if:
- Your team is familiar with Yarn from past projects
- You’re using React tooling that assumes Yarn (some older templates do)
- You want workspaces, and your organization already standardized on Yarn
- You’re ready to invest in Yarn Berry and possibly PnP for stricter dependency management
Yarn is tried and tested in very large codebases (think Meta, etc.).
7.3 When pnpm Shines
Use pnpm if:
- You’re building a monorepo with many packages
- Disk usage and install performance matter a lot (e.g., CI, many branches)
- You want stricter dependency resolution to catch bad imports
- You like the idea of a shared, content-addressable store
pnpm is especially attractive for:
- Design system repos (
packages/ui,packages/tokens, etc.) - Backend + frontend apps sharing common utilities
- Multi-app setups with shared libraries
8. Migrating Between Package Managers
You can often switch between them, but do it cleanly:
- Delete
node_modulesand the old lockfile(s) - Install using the new tool
- Commit the new lockfile
Example: migrate from npm to pnpm
rm -rf node_modules package-lock.json
pnpm installIt’s best to:
- Pick one per repo
- Document it in your README
- Avoid switching back and forth, or mixing tools (that leads to confusion and broken lockfiles)
9. Summary
npm, pnpm, and Yarn all solve the same core problem: managing JavaScript dependencies. But they do it with different trade-offs:
- npm: simple, stable, and ships with Node. Great default for many apps.
- Yarn: introduced faster, deterministic workflows, excellent workspace support, and advanced features in Berry.
- pnpm: aggressively efficient with disk and installs, ideal for monorepos and large codebases.
If you’re starting a new project today:
- For a single app or small project: npm or pnpm
- For a monorepo with multiple apps and packages: pnpm or Yarn workspaces
- For teams already standardized on something: stick with what your team knows, unless you have a strong reason to switch
Ultimately, the “best” package manager is the one your team understands and uses consistently, backed by good practices:
- Committed lockfiles
- Clear scripts
- Consistent workflow in local dev and CI
Pick one, learn it well, and let it disappear into the background while you focus on building great products.
Loading comments...