Two packages demand incompatible versions of a shared peer (React 17 vs 18 is the classic). npm 7+ enforces peers strictly — resolve the incompatibility or scope a legacy override deliberately.
Your app has react@18 but a dependency declares react "^17" as a peer. npm refuses to install a tree that breaks the declared contract. The error message shows both requirements.
package-lock.json predating the conflict, or both npm-shrinkwrap/package-lock/yarn.lock present, makes npm resolve against stale data. rm the stale file(s) once, then reinstall cleanly.
npm install 2>&1 | sed -n '/ERESOLVE/,/Fix/p' | head -15
npm install <ui-lib>@latest # a release with react-18 peer support, or downgrade react to satisfy the old lib
rm -rf node_modules package-lock.json && npm install # last resort, regenerates the tree fresh
"overrides": { "<ui-lib>": { "react": "$react" } } # explicit, version-controlled, visible in review
--legacy-peer-deps is the npm 6 behavior switch. Fine for unblocking, but it hides the conflict: put it in .npmrc only if the whole team agrees and knows why. CI tip: --legacy-peer-deps via env var changes resolution silently forever. The overrides field is auditable; the flag is not.
It disables peer checking: installs proceed even when contracts break, surfacing later as runtime errors (invalid hook calls, undefined components). Use it to unblock, then fix the versions properly.
npm 6 ignored peer conflicts (installed whatever, warned). npm 7+ auto-installs peers AND enforces their ranges. Same project, stricter referee.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.