Skip to content

Fix: pnpm Peer Dependency Errors (Missing or Incompatible Peer Dependencies)

FixDevs · (Updated: )

Part of:  JavaScript & TypeScript Errors

Quick Answer

How to fix pnpm peer dependency errors — why pnpm is stricter than npm about peer deps, how to resolve missing peers, configure peerDependencyRules, and handle incompatible version ranges.

The Error

Running pnpm install fails or warns with:

 WARN  Issues with peer dependencies found
.
└─┬ some-library 2.0.0
  ├── ✕ missing peer react@">=17.0.0"
  └── ✕ missing peer react-dom@">=17.0.0"

Or an error that blocks installation:

ERROR  unmet peer react@"^17.0.0": found 18.2.0

Or after installing:

 WARN  4 packages have unmet peer dependencies

Or a package works with npm but fails with pnpm:

Cannot find module 'react' from 'node_modules/some-library/dist/index.js'

Why This Happens

pnpm is stricter about peer dependencies than npm or yarn by default. In npm v7+, peer dependencies are automatically installed. pnpm takes a different approach:

  • Peer dependencies are not automatically installed — pnpm expects the consuming project to provide peer dependencies explicitly.
  • Isolated node_modules structure — pnpm uses a content-addressable store and symlinks. Packages cannot accidentally access peer dependencies through hoisting (as they can with npm’s flat node_modules).
  • Strict mode by default — pnpm reports peer dependency issues as errors in some configurations.

This is intentional. pnpm’s strictness prevents “phantom dependencies” — packages that work in development (because a peer is accidentally hoisted) but break in production.

In Production: Incident Lens

How the incident surfaces. Peer-dependency errors are deploy-time gating failures, not runtime crashes. The CI build runs pnpm install --frozen-lockfile, peer resolution fails on the build machine even though it succeeded on the engineer’s laptop, the deploy pipeline goes red, and the merge queue stalls. The version on the laptop usually had auto-install-peers=true filling the gap silently; the CI environment has tighter .npmrc configuration (or an older pnpm) that enforces strictness. The author sees “works on my machine” and wonders why CI alone is broken.

Blast radius. Bounded to the deploy pipeline, but the secondary effect is large. A single PR with a broken peer constraint can block every other PR on the merge queue if you use trunk-based development with rebase-on-merge — every subsequent PR rebases onto the broken commit and inherits the failure. The wider blast: dependency-bump PRs from Dependabot/Renovate often surface peer mismatches that block automated security patches, leaving CVE-vulnerable packages in production while the team manually unblocks. If pnpm install ran in the production image build (deployment process bakes node_modules into the image), the entire deploy pipeline stops.

The monitoring signal that catches it. CI build duration trend graphs catch slow degradation (peer-resolution thrashing inflates install time before outright failure). Direct signals: GitHub Actions failure notifications filtered on the “Install dependencies” step, Dependabot/Renovate dashboard for closed PRs with failing CI, and CI runs grouped by failure reason in tools like Buildkite or CircleCI. For pnpm specifically, a recurring pnpm install --strict-peer-dependencies job on the main branch (separate from the standard build) acts as an early-warning canary.

Recovery sequence. First, run pnpm install --strict-peer-dependencies 2>&1 | grep peer to get the full list of mismatches — the CI log often truncates. Second, classify each: (a) genuinely missing peer that should be added to the consuming app’s dependencies, (b) version mismatch on a package you control (upgrade the dep), (c) version mismatch on a package you don’t (use peerDependencyRules.allowedVersions). Third, the fastest unblock is usually adding auto-install-peers=true to .npmrc and committing it, then opening a follow-up PR to address the actual mismatches. Never solve this with shamefully-hoist=true as a permanent fix; you trade pnpm’s correctness guarantees for npm-shaped fragility.

Postmortem-style preventive. The durable controls: (1) pin the same pnpm version in CI and locally via packageManager in package.json so behavior matches; (2) .npmrc committed at the repo root with explicit auto-install-peers and strict-peer-dependencies values so behavior is reproducible; (3) Renovate grouped updates for peer-related dep families (React + react-dom + @types/react bumped together) so peer constraints stay aligned; (4) a pre-commit hook running pnpm install --frozen-lockfile so the author catches lockfile drift before CI does; (5) for monorepos, document the canonical peer dep versions in a top-level package.json and use Changesets or syncpack to enforce them.

Fix 1: Install the Missing Peer Dependencies

The simplest fix — install what the packages require:

# pnpm tells you what is missing:
# ✕ missing peer react@">=17.0.0"
# ✕ missing peer react-dom@">=17.0.0"

pnpm add react react-dom

For development-only peer deps (testing libraries, build tools):

pnpm add -D @testing-library/react @testing-library/jest-dom

Check what peer deps a package requires before installing it:

pnpm info some-library peerDependencies
# or
pnpm why some-library

After installing, verify peers are resolved:

pnpm install
# Should show no WARN lines about peer dependencies

Fix 2: Configure peerDependencyRules in .npmrc

For peer dependency warnings you cannot resolve (e.g., an incompatible version range from a dependency you do not control), configure pnpm to suppress or allow them:

# .npmrc

# Silence all peer dependency warnings (not recommended for production)
strict-peer-dependencies=false

# Allow specific peer dependency mismatches
# Format: package@range → allowed version range

Use peerDependencyRules in package.json:

{
  "pnpm": {
    "peerDependencyRules": {
      "ignoreMissing": [
        "webpack",
        "@babel/core"
      ],
      "allowedVersions": {
        "react": "18",
        "eslint": "8 || 9"
      },
      "allowAny": [
        "supports-color"
      ]
    }
  }
}

Explanation of each field:

  • ignoreMissing — suppresses “missing peer” warnings for these packages (you know you do not need them).
  • allowedVersions — tells pnpm that the installed version satisfies the peer requirement even if semver says otherwise. Use when you are confident the package works with your version.
  • allowAny — allows any version of the package to satisfy the peer requirement.
{
  "pnpm": {
    "peerDependencyRules": {
      "allowedVersions": {
        "react": "18.2.0",
        "react-dom": "18.2.0",
        "some-library>react": "18"
      }
    }
  }
}

The some-library>react format allows specifying the rule only for a specific package’s peer dependency — instead of globally.

Fix 3: Fix the “Cannot Find Module” Error at Runtime

pnpm’s isolated node_modules means a package cannot accidentally resolve peer dependencies through hoisting. If a package fails to find its peer at runtime:

Check how pnpm resolves the package:

pnpm why react
# Shows which packages depend on react and how it is resolved

Use shamefully-hoist as a last resort (mimics npm behavior):

# .npmrc
shamefully-hoist=true

This hoists all packages to the top-level node_modules like npm does. It defeats pnpm’s isolation benefits but fixes packages that rely on hoisting:

# Delete existing node_modules and reinstall with hoisting
rm -rf node_modules
pnpm install

Warning: shamefully-hoist=true is a workaround. It masks real dependency problems and makes your project fragile. Use it temporarily while you report or fix the upstream package, then remove it.

More targeted approach — hoist specific packages:

# .npmrc — hoist only specific packages instead of everything
hoist-pattern[]=*react*
hoist-pattern[]=*emotion*

Fix 4: Handle Peer Dependencies in Monorepos

In a pnpm workspace, peer dependencies must be installed at the right level:

# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
// packages/ui/package.json
{
  "name": "@myapp/ui",
  "peerDependencies": {
    "react": ">=17",
    "react-dom": ">=17"
  },
  "devDependencies": {
    "react": "18.2.0",   // Install as devDep for testing the package itself
    "react-dom": "18.2.0"
  }
}
// apps/web/package.json
{
  "name": "web",
  "dependencies": {
    "@myapp/ui": "workspace:*",
    "react": "18.2.0",       // Provides react to @myapp/ui as a peer
    "react-dom": "18.2.0"
  }
}

Install workspace packages correctly:

# From the root
pnpm install

# Add a dependency to a specific workspace package
pnpm add lodash --filter @myapp/ui

# Add a dev dependency to the root
pnpm add -D -w typescript

Check peer dependency status across the workspace:

pnpm -r exec -- pnpm ls --depth 0

Fix 5: Fix Version Range Conflicts

When two packages require incompatible versions of the same peer:

✕ package-a requires react@"^16.0.0"
✕ package-b requires react@"^18.0.0"
# Cannot satisfy both with a single react version

Option A — use allowedVersions to override (if you know it works):

{
  "pnpm": {
    "peerDependencyRules": {
      "allowedVersions": {
        "package-a>react": "18",
        "package-b>react": "18"
      }
    }
  }
}

Option B — upgrade package-a to a version that supports React 18:

pnpm outdated  # Shows available upgrades
pnpm update package-a --latest

Option C — replace package-a with a React 18-compatible alternative.

Option D — use overrides to force a specific version:

{
  "pnpm": {
    "overrides": {
      "react": "18.2.0",    // Forces all packages to use React 18.2.0
      "react-dom": "18.2.0"
    }
  }
}

Warning: Overrides force version resolution globally. A package requiring React 16 will receive React 18 — this may or may not work depending on the package’s actual compatibility. Test thoroughly.

Fix 6: Diagnose Peer Dependency Issues

See a full report of peer dependency issues:

pnpm install --strict-peer-dependencies 2>&1 | grep -E "WARN|ERROR|peer"

List all packages with unmet peers:

pnpm ls --depth Infinity 2>&1 | grep "unmet peer"

Check why a specific package is installed:

pnpm why some-package
# Shows the dependency chain that requires some-package

Dry run to see what pnpm will install:

pnpm add some-library --dry-run

Fix 7: Migrate from npm to pnpm Without Breaking Peer Deps

When migrating an existing project from npm to pnpm:

# 1. Remove npm artifacts
rm -rf node_modules package-lock.json

# 2. Install pnpm
npm install -g pnpm

# 3. Import from package.json (generates pnpm-lock.yaml)
pnpm import  # Converts package-lock.json or yarn.lock to pnpm-lock.yaml

# 4. Install
pnpm install

# 5. Check for peer dep warnings
# Address each warning or add to peerDependencyRules

Add a .npmrc with sensible defaults for migrated projects:

# .npmrc
auto-install-peers=true          # Automatically install missing peers (pnpm 8+)
strict-peer-dependencies=false   # Warn but don't error on peer dep issues during migration

auto-install-peers=true (pnpm 8 default) makes pnpm behave more like npm by automatically installing missing peer dependencies. This is the easiest fix for most migration issues.

Still Not Working?

Check your pnpm version. pnpm 8 changed peer dependency behavior significantly — auto-install-peers defaults to true in pnpm 8, which resolves many issues automatically:

pnpm --version
# If < 8, upgrade:
npm install -g pnpm@latest

Check for conflicting .npmrc files. A .npmrc in your home directory (~/.npmrc) may conflict with the project .npmrc:

cat ~/.npmrc
cat .npmrc

Regenerate the lockfile if it is corrupted:

rm pnpm-lock.yaml
pnpm install

Check if the issue is specific to CI. CI environments sometimes have different Node.js versions or global packages that affect peer resolution. Pin Node.js version in CI:

# .github/workflows/ci.yml
- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'pnpm'

For related package manager issues, see Fix: npm ERR! peer dep conflict, Fix: npm ERESOLVE Unable to Resolve Dependency Tree, Fix: npm ENOENT No Such File, and Fix: Yarn Integrity Check Failed.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles