The Mystical World of npm Dependencies, peer-dependencies, legacy peer dependencies and why I should stop using npm i —-force
So if you’ve worked with npm and installed packages or plugins, you might’ve come across an error that looks something like this:
Or maybe they were just warnings, or you used
—legacy-peer-deps or —force with your npm I command to just make it work.
I faced these issues a couple of times, and it got me thinking:
What on Earth is a peer dependency?
How are they different from regular dependencies?
What do
—legacy-peer-depsand--forcereally doAnd do they break anything?
So I went on a hunt for knowledge and this is what I came up with:
Understanding npm Dependencies and Peer Dependencies — In an intuitive way, baking:
Imagine we’re baking a cake (our app) and to make it happen, we have a cookbook with a recipe for it (that’s our package.json).
(Yes, that image in AI generated, because why not...)
To make it even more delicious, we bought a tub of super fancy buttercream frosting 😋(that’s a third‑party plugin/package that we want to add to our app).
So our recipe lists the things it needs to help us bake our cake properly: flour, eggs, sugar, butter. These are our dependencies. (they are a part of the “dependencies” section in our package.json) We bring them into the kitchen and use them directly to make the cake batter.
Since the frosting we bought is made by someone else and is super fancy, it is designed and tested to emulsify and stay stable/work with a particular kind of butter (say FancyButter v1). So, instead of shipping its own butter, the frosting declares:
“I need you (the cake) to already provide SoftPark Butter v1.”
That declaration is a peer dependency.
Although I like cakes and would like to talk about (and eat them) more, let’s look at this from a technical perspective to finish the analogy.
I’ll use the error image from above as an example:
This project (which is actually this blog site haha) uses React 19 with all its cool SSR and latest features.
But, one of our dependencies,
react-mde(a markdown editor for react) that we use to write and format these blogs on the site, says that it works with React version 17 (peerreact@"^17.0.0"). [The dependencyreact-mde, has a peer dependencyreact 17that it expects our app to provide and already have].Since npm doesn’t know how to resolve these conflicting dependencies, it raises an ERESOLVE, crying, “Help I don’t know how to resolve this :(“
One note here is that we’re using npm version 7+, and you probably are as well. Npm versions 7 onwards added a strict dependency resolver (or a strict kitchen manager in case you’re still following the cake analogy. So, before serving, they check: “Does the cake’s butter match the frosting’s requirement?” If not, they stop you and say it’s unsafe to proceed.)
Internally, npm’s dependency tree is just a graph of packages. If you looked at a visual representation of a dependency graph, you’ll see that dependencies go downwards, whereas peer dependencies can point upwards or even sideways (that’s why they are called “peer” dependencies)
Npm versions before 7 weren’t that strict and were fine with potentially broken dependency graphs, it was like an old manager who didn’t care — they let the frosting bring its own butter jar. Sometimes it worked, sometimes the frosting split later.
If you’d like to use npm’s older peer dependency resolution algorithm- the one that didn’t auto-install peer deps and allowed for potentially broken dependency graphs and duplicate packages- just so that you can go on with your day, you can use npm install —legacy-peer-deps.
The —-force flag will go ahead and install all peer dependencies, fetch packages from a remote repository even if they exist locally (like a fresh install), and also ignore any other safety checks. For these reasons, it is generally not recommended to use the --force flag.
Why two versions of the same dependency can cause trouble
Split frosting / failed emulsification
In cooking: Your frosting curdles because you mixed ingredients at different temperatures — they can’t bind into one smooth texture.
In code: React hooks, context, or other shared singletons break when two versions are loaded, because each copy keeps its own separate internal state.Different flavor & texture
In cooking: If one butter is salted, and one is unsalted — your cake ends up tasting inconsistent.
In code: Different versions of a library may have API or behavior changes, so parts of your app expect different “flavors” of functionality.Bigger pantry & wasted ingredients
In cooking: You stock two kinds of the same butter, taking up more shelf space than necessary.
In code: Bundling both versions increases your JavaScript bundle size and wastes memory.Harder to debug issues
In cooking: You taste the cake and can’t tell which butter ruined it — both were in the mix.
In code: When a bug appears, it’s hard to trace which version is responsible because stack traces and runtime behavior may differ across versions.
So… does —-legacy-peer-deps or —-force break anything?
Yes — sometimes immediately, sometimes weeks later when you're not looking. Peer mismatches are subtle troublemakers — like expired butter: not noticeable at first, but inevitably disastrous.
So, how do we fix it? (without burning the kitchen of course)
Match versions & resolve manually — For example, If a package expects React 17, see if there's an updated version of that package for React 19.
Read the warnings — As much as it feels like it, npm isn’t annoying you; it's trying to guide you.
Avoid --force - unless you really know what you’re overriding.
It’s recommended to use --legacy-peer-deps only as a temporary local hack, never in production builds.
Use --strict-peer-deps can help you gain clarity — it turns peer mismatches into hard stops.
Consider package overrides or switches — sometimes changing libraries is the path of least resistance. More info at: DEV Community, Reddit
Pro tip: You can use
npm ls <pkg>to inspect where multiple versions are coming from.
Final Slice of Wisdom (pun intended.)
Using npm i --force as a “just make it work” trick is like duct-taping a wobbly cake stand: you'll get it to the table, but don’t be surprised when it collapses mid-party.
Next time npm cries ERESOLVE, pause, check your ingredients, and align your versions. Your cake — and your future self — will thank you.
Hope this article helped you understand what npm dependencies, and peer-dependencies are, and when you should use --force and --legacy-peer-deps , or atleast made you want to get some cake (hidden agenda alert). Thanks for reading! Feel free to connect with us on LinkedIn and GitHub! :)
Some awesome references that helped me understand this better 🙏(credit goes to them):
https://docs.npmjs.com/cli/v7/commands/npm-install
https://stackoverflow.com/questions/66020820/npm-when-to-use-force-and-legacy-peer-deps
https://dev.to/icy0307/peer-dependencies-in-depth-1o3b
Honorable Mention: ChatGPT 😆