Introduction

Starting with npm v7, peer dependencies are installed by default and strictly resolved. When two packages require different major versions of the same peer dependency, npm refuses to install with ERESOLVE unable to resolve dependency tree. This is more strict than npm v6 and Yarn but prevents incompatible package combinations. Common conflicts occur with React, TypeScript, ESLint, and testing libraries.

Symptoms

  • npm ERR! ERESOLVE unable to resolve dependency tree
  • npm ERR! Could not resolve dependency: peer react@"^16.8.0" from react-dom@16.14.0
  • npm ERR! Conflicting peer dependency: react@18.2.0
  • npm install fails after adding a new package
  • Works with --legacy-peer-deps but not with default settings
bash
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: my-app@1.0.0
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR!   react@"^18.2.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.0" from react-native-modal@13.0.0
npm ERR! node_modules/react-native-modal
npm ERR!   react-native-modal@"^13.0.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps

Common Causes

  • Upgrading React from v16 to v18 while some packages only support v16
  • Mixing ESLint v8 and v9 plugins with different peer requirements
  • Testing library version mismatches
  • Monorepo workspaces with conflicting peer dependencies
  • Third-party packages with outdated peer dependency declarations

Step-by-Step Fix

  1. 1.Understand the conflict tree:
  2. 2.```bash
  3. 3.# Show why npm cannot resolve dependencies
  4. 4.npm install 2>&1 | head -50

# Show dependency tree npm ls react npm ls eslint

# Find which packages require which versions npm explain react ```

  1. 1.Upgrade the conflicting package:
  2. 2.```bash
  3. 3.# Check for updates to the package with strict peer deps
  4. 4.npm outdated react-native-modal
  5. 5.npm install react-native-modal@latest
  6. 6.# Newer versions may support React 18
  7. 7.`
  8. 8.Use npm overrides to force a version:
  9. 9.```json
  10. 10.// package.json
  11. 11.{
  12. 12."overrides": {
  13. 13."react": "$react",
  14. 14."react-dom": "$react-dom"
  15. 15.},
  16. 16."dependencies": {
  17. 17."react": "^18.2.0",
  18. 18."react-dom": "^18.2.0",
  19. 19."react-native-modal": "^13.0.0"
  20. 20.}
  21. 21.}

// "$react" resolves to the version in dependencies // This forces all packages to use React 18 ```

  1. 1.Use --legacy-peer-deps (temporary workaround):
  2. 2.```bash
  3. 3.# Skip peer dependency resolution (npm v6 behavior)
  4. 4.npm install --legacy-peer-deps

# Or set permanently in .npmrc echo "legacy-peer-deps=true" >> ~/.npmrc

# WARNING: This may install incompatible package combinations # Only use when you have verified the packages work together ```

  1. 1.Use npm install --force for one-time resolution:
  2. 2.```bash
  3. 3.# Force installation despite conflicts
  4. 4.npm install --force

# This creates a nested node_modules for the conflicting package # May result in duplicate packages in node_modules ```

  1. 1.Configure for monorepo workspaces:
  2. 2.```json
  3. 3.// Root package.json
  4. 4.{
  5. 5."workspaces": ["packages/*"],
  6. 6."overrides": {
  7. 7."react": "^18.2.0",
  8. 8."typescript": "^5.3.0"
  9. 9.}
  10. 10.}

// npm will hoist these versions to the root // All workspaces share the same peer dependency versions ```

Prevention

  • Keep peer dependencies updated when upgrading major versions
  • Use npm overrides to enforce consistent versions across the tree
  • Add npm install to CI to catch conflicts before deployment
  • Check package peer dependency compatibility before installing
  • Prefer --legacy-peer-deps only for packages known to be compatible
  • Use npm explain <package> to understand dependency chains
  • Consider using pnpm which handles peer dependencies more flexibly