Dependencies · Debugging

ERESOLVE unable to resolve dependency tree in React Native

Published April 22, 2026 · 9 minute read
TL;DR

ERESOLVE is npm 7+ telling you a peer dependency conflict can't be auto-resolved. It's not a bug. It's the resolver doing its job.

The fast fix is npm install --legacy-peer-deps. The right fix is reading the error, finding the package whose peer range doesn't match your react or react-native version, and pinning or upgrading the actual conflict.

If you're hitting ERESOLVE every install, your dependency graph has a problem you're papering over. It catches up eventually.

You ran npm install. You got this:

npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: [email protected] npm ERR! Found: [email protected] 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@"^17.0.0" from [email protected]

That's the message. This post is about what it actually means, the four ways to fix it, and the one that buys you a week before it explodes again.

On this page
  1. The 30-second fix
  2. What ERESOLVE actually means
  3. Why React Native hits this constantly
  4. Read the error properly
  5. The four real fixes
  6. The shortcut and when it backfires
  7. Stop hitting it next time
  8. Quick reference
  9. FAQ

The 30-second fix

If you need to ship in the next ten minutes:

npm install --legacy-peer-deps

This tells npm to behave like npm 6 and ignore peer dependency conflicts. The install completes. Your app probably runs. Whether it actually works is a separate question.

Use it. Then come back and read the rest of this post before the next install.

Heads up

--legacy-peer-deps doesn't fix the conflict. It hides it. If the peer mismatch was real — say, a library calling a hook that doesn't exist in your React version — you'll find out at runtime. Often during a demo.

What ERESOLVE actually means

npm 7 introduced a stricter resolver. When two of your dependencies declare incompatible peer dependency ranges, npm stops at install time instead of picking a winner and hoping.

Older npm just shrugged. Whatever got installed last won. You'd discover the broken combination at runtime when something silently crashed.

The npm 7+ contract: peer dependencies are real constraints. If two packages can't share a version, you have a real problem. ERESOLVE is the resolver refusing to ship a known-broken graph.

It's a stricter rule, not a new failure mode. The bugs were always there.

Why React Native hits this constantly

React Native pins react exactly. Every release. Look at any React Native release notes — there's a "react" line with a specific version. 0.74.0 ships with [email protected]. 0.76.0 ships with [email protected]. The version isn't a suggestion.

Every library in the React Native ecosystem declares react and react-native as peer dependencies. Their authors pick a peer range based on what they tested against.

Mix three libraries:

You upgrade React Native. ERESOLVE.

Add Expo and the surface widens. Expo SDK pins its own React Native version. expo@~50 wants [email protected]. If you've installed a community library that wants [email protected]+, the resolver catches you.

This isn't a React Native bug. It's the cost of an ecosystem where the platform pins things tightly and the libraries don't always keep up.

Read the error properly

Most people skim the first line of an ERESOLVE error and reach for --legacy-peer-deps. The error tells you exactly what's wrong if you actually read it.

Three things to find:

  1. What's installed. The "Found:" line. Found: [email protected] — that's what you have.
  2. What's wanted. The "peer" line. peer react@"^17.0.0" from [email protected] — that's the conflict.
  3. Who wants it. The package name and version after "from". This is the package you need to fix or replace.

If npm gives you a "Conflicting peer dependency" block lower down, read that too. It tells you which other package is requiring the version you have, so you can see both sides.

Now you know which library is out of date, what version it needs, and what version everything else needs. That's enough to pick a real fix.

The four real fixes

Fix 01

Upgrade the offending package

Best case. The library has a newer version that supports your React or React Native version.

npm view some-old-library versions --json npm install some-old-library@latest

Check the changelog. Confirm the new version actually supports your React Native version, not just the latest one. Library authors aren't always great at maintaining peer ranges across multiple supported branches.

Fix 02

Replace the package

The library hasn't been updated in 18 months. The maintainer has moved on. The peer range will never get fixed.

This is more common than you'd think. npm view some-package time.modified tells you when it was last published. If that's more than a year ago and the open issues page is full of "doesn't work with React Native 0.74" reports, the package is dead.

Find a maintained alternative. Or fork it, fix the peer range, publish under your scope. Forking a tiny library is often the path of least pain.

Fix 03

Use overrides in package.json

Sometimes the peer range is wrong, not the package. The library actually works on react@18, the author just never updated peerDependencies.

Add an overrides block:

{ "overrides": { "some-old-library": { "react": "18.2.0", "react-native": "0.74.0" } } }

Targeted. Documented. Survives reinstalls. Better than a global --legacy-peer-deps because it only affects the specific package you've actually verified works.

Yarn calls this resolutions. Same idea, different syntax.

Fix 04

Downgrade your React Native or React version

Last resort. You're on a bleeding-edge React Native version and a load-bearing library hasn't caught up. The hole isn't getting plugged this sprint.

Pin React Native back to a version the rest of your graph supports. Plan the upgrade properly later. Read the React Native upgrade playbook before you try again.

This is the option people resist because it feels like going backwards. It's not. It's recognizing that the upgrade you started wasn't planned, and a planned upgrade does better than a broken one.

The shortcut and when it backfires

--legacy-peer-deps works. --force also works. They're not the same thing.

--legacy-peer-deps says "ignore peer dependency conflicts." That's narrow. Other safety checks still run.

--force says "override anything in your way." Integrity check failures, version range mismatches, peer conflicts — all of it. It's a sledgehammer. Reach for it only when you know exactly what you're forcing past, and why.

The pattern that backfires: a developer hits ERESOLVE, runs --legacy-peer-deps, the install succeeds, the app boots, they ship. Three weeks later a different developer adds a feature that touches the conflicting library. Crash on Android only. The peer-dep mismatch was real — the library's iOS code path didn't use the missing API, the Android one did.

The flag suppressed the resolver's warning. The bug was always going to ship.

If you use --legacy-peer-deps, treat it as a TODO. Add a comment in package.json or a NOTES.md entry: which package, which conflict, when you'll come back. Future you will need it.

Stop hitting it next time

Five practices that keep ERESOLVE off your install loop:

Quick reference

Command / flagWhat it doesWhen to use
npm install --legacy-peer-depsIgnores peer dependency conflicts (npm 6 behavior)Triage. When you've read the error and the conflict is cosmetic
npm install --forceOverrides every safety check, not just peer depsAlmost never. Only when you know exactly what you're forcing
npm view PACKAGE peerDependenciesShows the package's declared peer rangesBefore installing. Catches conflicts in advance
npm view PACKAGE time.modifiedShows when the package was last publishedTo assess whether the library is maintained
overrides in package.jsonPins a transitive dependency to a specific versionWhen a library's peer range is wrong but the library itself works
npm ls reactShows every version of react in your dependency treeDiagnosing a duplicate-react bug after a "successful" install
Our free scannerCVE + outdated package check for React Native lockfilesBefore an upgrade. Identifies stale libraries that'll cause ERESOLVE

Hitting ERESOLVE on every install?

That's a signal your dependency graph has drifted. Run the free scanner against your lockfile — you'll get a ranked list of CVEs, outdated packages, and abandoned libraries in about 90 seconds.

Frequently Asked Questions

What does ERESOLVE unable to resolve dependency tree mean?

It means npm 7+ found a peer dependency conflict it can't resolve automatically. One package wants react@18, another wants react@17, and npm refuses to pick a winner. Older npm versions silently installed whatever they got last and let you discover the bug at runtime. npm 7+ stops at install time. It's a stricter contract, not a new failure mode.

Should I use --legacy-peer-deps to fix ERESOLVE in React Native?

Sometimes. Use it when the peer-dep mismatch is cosmetic — a library that says it wants react@17 but actually works fine on react@18. Don't use it when the conflict is real — for example, [email protected] paired with a package that calls APIs only present in [email protected]. The flag suppresses the error; it doesn't fix the incompatibility. If you reach for it, document why in package.json or a README, so the next person doesn't inherit a silent landmine.

What's the difference between --legacy-peer-deps and --force?

--legacy-peer-deps tells npm to behave like npm 6: ignore peer dependency conflicts entirely. --force is broader — it overrides any conflict, including version range mismatches and integrity check failures. --legacy-peer-deps is the right tool for ERESOLVE in 95% of cases. Reach for --force only when you know exactly what you're forcing and why.

Why does React Native hit ERESOLVE so often?

React Native pins react to a specific version per release. Every library in the React Native ecosystem declares react and react-native as peer dependencies. When you mix libraries built for different React Native versions, the peer ranges collide. Add Expo (which pins its own react version) and the surface area for collisions is wide. The npm 7+ resolver catches all of it instead of letting it slide.

Should I switch to yarn or pnpm to avoid ERESOLVE?

Switching package managers won't fix the underlying conflict. Yarn 1 ignores peer-dep conflicts more permissively, which can hide the problem until runtime. Yarn 3+ and pnpm both enforce peer dependencies, just with different defaults. Pick a package manager based on what your team and CI already use. Don't switch tools to dodge an error — fix the dependency graph.

How do I prevent ERESOLVE from happening on every install?

Pin react and react-native exactly (no caret ranges). Use overrides in package.json to lock transitive versions when a library author hasn't published a fix. Run npm install in CI on every PR so conflicts surface in review, not on a teammate's laptop the day they onboard. Audit dependencies before adding them — check that their peer ranges include your current React Native version.