Resolve Peer Dependency Conflicts in Angular Projects
Ever encountered this frustrating error when trying to install dependencies?
npm ERR! code ERESOLVEnpm ERR! ERESOLVE unable to resolve dependency treenpm ERR!npm ERR! While resolving: ultimate-starter@0.0.0npm ERR! Found: @angular/cli@12.2.14npm ERR! node_modules/@angular/clinpm ERR! dev @angular/cli@"^12.2.14" from the root projectnpm ERR!npm ERR! Could not resolve dependency:npm ERR! peer @angular/cli@">= 13.0.0 < 14.0.0" from @angular-eslint/schematics@13.0.1npm ERR! node_modules/@angular-eslint/schematicsnpm ERR! dev @angular-eslint/schematics@"^13.0.1" from the root projectThis 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:
- Version mismatches: Your project has a different version than what a package requires
- Strict version ranges: Some packages use strict peer dependency ranges (like
>= 13.0.0 < 14.0.0) - Multiple packages requiring different versions: Different packages might require incompatible versions of the same peer dependency
In our example:
- Your project uses
@angular/cli@12.2.14 @angular-eslint/schematics@13.0.1requires@angular/cli@>= 13.0.0 < 14.0.0- These ranges don't overlap, causing the conflict
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:
- npm strictly enforces peer dependency requirements by default
- If a peer dependency conflict is detected, npm will fail the installation
- You can use
--legacy-peer-depsto ignore peer dependency conflicts (npm v7+) - Or use
--forceto override conflicts (not recommended)
Example:
# This will fail with our conflictnpm install
# This will ignore peer dependency conflictsnpm install --legacy-peer-deps
# This will force installation (risky)npm install --forceyarn
Yarn (v1) uses a similar flattened approach but with more flexibility.
Peer dependency handling:
- Yarn v1 is more lenient with peer dependencies
- It may install packages even with peer dependency warnings
- Warnings are shown but don't block installation by default
- You can use
--ignore-peer-depsto skip peer dependency checks
Example:
# May show warnings but continueyarn install
# Skip peer dependency checks entirelyyarn install --ignore-peer-depsYarn 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:
- pnpm is very strict about peer dependencies
- It will fail if peer dependencies aren't satisfied
- However, it provides better error messages
- You can use
--shamefully-hoistto flatten the tree (defeats pnpm's benefits) - Or use
.npmrcconfiguration to adjust behavior
Example:
# This will fail with our conflictpnpm install
# Flatten dependencies (not recommended)pnpm install --shamefully-hoistSolutions
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:
# Update Angular CLI to version 13npm install --save-dev @angular/cli@^13.0.0
# Then install your other dependenciesnpm installSolution 2: Use Compatible Package Versions
If you can't upgrade, use a version of the conflicting package that's compatible with your current setup:
# Use an older version of @angular-eslint/schematics compatible with Angular CLI 12npm install --save-dev @angular-eslint/schematics@^12.0.0Solution 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:
npm install --legacy-peer-depsThis 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:
# Create or update .npmrc in your project rootecho "legacy-peer-deps=true" >> .npmrcNow 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
- Keep dependencies up to date: Regularly update your dependencies to avoid conflicts
- Check peer dependencies before installing: Review a package's peer dependencies before adding it
- Use exact versions for critical dependencies: Consider pinning versions for critical packages
- Test after resolving conflicts: Always test your application after using workarounds like
--legacy-peer-deps - Document your workarounds: If you use flags or overrides, document why in your README or team docs
When to Use Each Solution
- Update dependencies: When you can upgrade and want the cleanest solution
- Use compatible versions: When you're locked to a specific version and can't upgrade
- Use legacy-peer-deps: As a temporary workaround while planning an upgrade
- Use overrides/resolutions: When you need fine-grained control over specific packages
- Use pnpm peerDependencyRules: When using pnpm and need flexible peer dependency handling
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.