angular.courses

Resolve Peer Dependency Conflicts in Angular Projects

Ever encountered this frustrating error when trying to install dependencies?

Terminal window
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: ultimate-starter@0.0.0
npm ERR! Found: @angular/cli@12.2.14
npm ERR! node_modules/@angular/cli
npm ERR! dev @angular/cli@"^12.2.14" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/cli@">= 13.0.0 < 14.0.0" from @angular-eslint/schematics@13.0.1
npm ERR! node_modules/@angular-eslint/schematics
npm ERR! dev @angular-eslint/schematics@"^13.0.1" from the root project

This is a classic peer dependency conflict. Your project uses @angular/cli@12.2.14, but @angular-eslint/schematics@13.0.1 requires @angular/cli version 13 or higher.

Let's dive into why this happens, how different package managers handle it, and what you can do about it.

What are Peer Dependencies?

Peer dependencies are packages that your library expects the consuming application to provide. Unlike regular dependencies, they're not automatically installed by the package manager.

Think of them as a contract: "I need this package to work, but I expect you (the application) to provide it."

For example, @angular-eslint/schematics declares @angular/cli as a peer dependency because it needs the Angular CLI to function, but it doesn't want to bundle its own version. This prevents version conflicts and keeps your node_modules cleaner.

Why Conflicts Happen

Peer dependency conflicts occur when:

  1. Version mismatches: Your project has a different version than what a package requires
  2. Strict version ranges: Some packages use strict peer dependency ranges (like >= 13.0.0 < 14.0.0)
  3. Multiple packages requiring different versions: Different packages might require incompatible versions of the same peer dependency

In our example:

How Package Managers Resolve Dependencies

Each package manager handles dependency resolution differently, which affects how they deal with peer dependencies.

npm

npm uses a flattened dependency tree approach. It tries to install all dependencies at the root level when possible, hoisting them up to avoid duplication.

Peer dependency handling:

Example:

Terminal window
# This will fail with our conflict
npm install
# This will ignore peer dependency conflicts
npm install --legacy-peer-deps
# This will force installation (risky)
npm install --force

yarn

Yarn (v1) uses a similar flattened approach but with more flexibility.

Peer dependency handling:

Example:

Terminal window
# May show warnings but continue
yarn install
# Skip peer dependency checks entirely
yarn install --ignore-peer-deps

Yarn v2+ (Berry) has stricter peer dependency handling, more similar to npm v7+.

pnpm

pnpm uses a content-addressable store with a strict dependency resolution strategy.

Peer dependency handling:

Example:

Terminal window
# This will fail with our conflict
pnpm install
# Flatten dependencies (not recommended)
pnpm install --shamefully-hoist

Solutions

Here are practical solutions to resolve peer dependency conflicts:

Solution 1: Update Your Dependencies

The cleanest solution is to align versions. If @angular-eslint/schematics@13.0.1 requires @angular/cli@>= 13.0.0, update your Angular CLI:

Terminal window
# Update Angular CLI to version 13
npm install --save-dev @angular/cli@^13.0.0
# Then install your other dependencies
npm install

Solution 2: Use Compatible Package Versions

If you can't upgrade, use a version of the conflicting package that's compatible with your current setup:

Terminal window
# Use an older version of @angular-eslint/schematics compatible with Angular CLI 12
npm install --save-dev @angular-eslint/schematics@^12.0.0

Solution 3: Use npm's Legacy Peer Deps Flag

For npm v7+, you can use the --legacy-peer-deps flag to use the old (pre-v7) peer dependency resolution:

Terminal window
npm install --legacy-peer-deps

This tells npm to ignore peer dependency conflicts and install anyway. Use with caution - your application might break if the peer dependency version mismatch causes runtime issues.

Solution 4: Configure npm to Always Use Legacy Peer Deps

You can configure npm to always use legacy peer dependency resolution by creating or updating .npmrc:

Terminal window
# Create or update .npmrc in your project root
echo "legacy-peer-deps=true" >> .npmrc

Now all npm install commands will use legacy peer dependency resolution.

Solution 5: Use package.json's overrides (npm) or resolutions (yarn)

You can force specific versions to resolve conflicts:

For npm (v8.3+):

{
"overrides": {
"@angular/cli": "^13.0.0"
}
}

For yarn:

{
"resolutions": {
"@angular/cli": "^13.0.0"
}
}

For pnpm:

{
"pnpm": {
"overrides": {
"@angular/cli": "^13.0.0"
}
}
}

Solution 6: Use peerDependencyRules (pnpm)

pnpm allows you to configure peer dependency rules in package.json:

{
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"@angular/cli": "12 || 13"
},
"ignoreMissing": ["@angular/cli"]
}
}
}

Best Practices

  1. Keep dependencies up to date: Regularly update your dependencies to avoid conflicts
  2. Check peer dependencies before installing: Review a package's peer dependencies before adding it
  3. Use exact versions for critical dependencies: Consider pinning versions for critical packages
  4. Test after resolving conflicts: Always test your application after using workarounds like --legacy-peer-deps
  5. Document your workarounds: If you use flags or overrides, document why in your README or team docs

When to Use Each Solution

Conclusion

Peer dependency conflicts are a common challenge in Angular projects, especially when working with third-party libraries that have strict version requirements. Understanding how your package manager handles these conflicts helps you choose the right solution.

Remember: while workarounds like --legacy-peer-deps can unblock you, the best long-term solution is to keep your dependencies aligned and up to date.