Next.js

Material UI install fails on React 18 or 19 (ERESOLVE): fix

See why npm refuses the old @material-ui packages on a React 18 or 19 project, which packages to install instead, and why --legacy-peer-deps on React 19 only moves the failure to runtime.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

TL;DR#

npm install @material-ui/core @material-ui/icons installs Material UI v4, and both packages declare peer react@"^16.8.0 || ^17.0.0". A project on React 18, or on React 19 as a new project gets today, is outside that range, so npm 7 and later stops with ERESOLVE unable to resolve dependency tree. Nothing in your setup is broken: those are simply the wrong package names. Install the renamed packages, npm install @mui/material @emotion/react @emotion/styled and then npm install @mui/icons-material, which accept React 17, 18 and 19. Do not force v4 in with --legacy-peer-deps: on React 19 it installs, then crashes at runtime.

The error, then and now#

The report comes from a Stack Overflow question asked in April 2022, days after React 18 shipped: a fresh Create React App project, then npm install @material-ui/core @material-ui/icons, then a wall of npm ERR!. Clearing the npm cache did not help. The same thing still happens to anyone who follows an old tutorial or copies an import from a v4 codebase, because those packages remain on npm under their old names.

The conflict was reproduced on 24 October 2026 in a throwaway node:24-alpine container (Node 24.21.0, npm 11.19.0). Current npm prints npm error where npm 8 printed npm ERR!, but the diagnosis is the same. On a project whose package.json asks for React ^18.0.0:

text
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error While resolving: r18@1.0.0
npm error Found: react@18.3.1
npm error Could not resolve dependency:
npm error peer react@"^16.8.0 || ^17.0.0" from @material-ui/core@4.12.4

And on a project created today, where npm install react react-dom resolves the current React 19 release:

text
npm error Found: react@19.3.1
npm error   react@"^19.3.1" from the root project
npm error peer react@"^16.8.0 || ^17.0.0" from @material-ui/core@4.12.4
npm error Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution.

Running npm cache clean --force and retrying, as the original poster did, gives the same ERESOLVE lines. The cache is not involved: npm is comparing two version ranges, and they do not overlap.

Why npm refuses Material UI v4#

Two facts meet in that message.

The packages are v4, and v4 is finished. The npm registry still serves @material-ui/core at 4.12.4, with a deprecation notice that reads "Material UI v4 doesn't receive active development since September 2021." Its manifest declares React ^16.8.0 || ^17.0.0 as a peer. @material-ui/icons 4.11.3 declares the same React range plus a peer on @material-ui/core v4. None of the 162 versions in the full @material-ui/core registry document lists React 18 or 19 in its React peer range. MUI's v4 to v5 migration guide lists the rename: @material-ui/core became @mui/material and @material-ui/icons became @mui/icons-material.

npm 7 and later enforce peer ranges. A peer dependency is a statement by a library about the host package it must share with your app, here React. npm's peerDependencies reference explains that npm 7 and later install peers automatically; when the React already in your project falls outside a declared peer range, the install stops instead. React 18.3.1 or 19.3.1 cannot satisfy ^16.8.0 || ^17.0.0, so it stops. The page on redux-form rejecting React 17 reads the same kind of report line by line, Found first, then the package whose peer range refuses it; the method is identical here.

Fix: install the renamed packages#

Steps 2 and 3 were run in the same container, as one combined npm install of the four packages, on a fresh React 19 project and, separately, on a React 18.3.1 project with step 4 applied first. Step 1 only matters if v4 entries are already in your package.json.

  1. Remove the v4 packages if a failed attempt or an old package.json left them behind:
bash
npm uninstall @material-ui/core @material-ui/icons
  1. Install Material UI and its default styling engine, Emotion, exactly as the Material UI installation guide gives it:
bash
npm install @mui/material @emotion/react @emotion/styled
  1. Install the icons, if you use them:
bash
npm install @mui/icons-material
  1. On React 18 only: the installation guide for the current release says that on React 18 or below you must pin react-is to the same version as react. With react@18.3.1, install react-is@18.3.1, then add an override to package.json:
json
{
  "overrides": {
    "react-is": "^18.3.1"
  }
}
  1. Change the imports in your code from the old names to the new ones:
jsx
import Button from '@mui/material/Button';
import DeleteIcon from '@mui/icons-material/Delete';

On React 19, @mui/material 9.5.0 installed with exit code 0 next to react@19.3.1, and @mui/icons-material resolved to the same version. On the React 18 project with the override in place, the tree resolved to a single react@18.3.1 and a single react-is@18.3.1, with no ERESOLVE. The current @mui/material manifest declares React ^17.0.0 || ^18.0.0 || ^19.0.0 as its peer range, which is why both installs succeed without any flag. Do not pin an early v5 instead: the registry history of @mui/material shows 5.0.0 asked for React ^17.0.2, React 18 entered the range in 5.6.0 and React 19 in 5.16.8.

Check your own npm conflict#

Paste the Found: and Could not resolve dependency: lines from your terminal to see which package rejects which version. It runs in your browser; nothing is sent.

Everything runs in your browser — this tool makes no network request with what you paste, and file paths from the log are never shown back. Registry tokens or passwords that slip into a paste are masked in the output.

Verify the fix#

First, ask npm whether the tree is valid. npm ls exits non-zero and prints invalid next to any package whose peer range is not met:

bash
npm ls react @mui/material @mui/icons-material

On the React 19 test project the output listed @mui/material@9.5.0 and @mui/icons-material@9.5.0, with every React reference deduped to react@19.3.1 and no invalid marker.

Then render something. A server-side render is enough to prove the packages load and work together, without a browser:

js
import { createElement as h } from 'react';
import { renderToString } from 'react-dom/server';
import Button from '@mui/material/Button';
import DeleteIcon from '@mui/icons-material/Delete';
const html = renderToString(h(Button, { variant: 'contained', startIcon: h(DeleteIcon) }, 'Delete'));
console.log(html.slice(0, 120));

Saved as render.mjs and run with node render.mjs, it printed the Emotion style tag for the button on both the React 19 and the React 18 projects:

text
<style data-emotion="css 1x9p500-MuiButton-root">.css-1x9p500-MuiButton-root{font-family:"Roboto","Helvetica","Arial",sa

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

Why --legacy-peer-deps and --force are the wrong fix here#

The third answer on the question forces v4 in with npm i @material-ui/core --legacy-peer-deps, and npm's own message suggests --force. The top-voted answer used the same flag on @mui/material, whose registry history shows releases 5.5.0 to 5.5.3 still capped React at ^17.0.0; since 5.6.0 that flag is unnecessary for the new packages. Applied to v4, both flags make the error disappear, and they defer it rather than fix it.

npm's definition of the option says it "Causes npm to completely ignore peerDependencies when building a package tree" and that its use "is not recommended, as it will not enforce the peerDependencies contract that meta-dependencies may rely on." Here is what that means in practice. On a React 19 project, npm install @material-ui/core @material-ui/icons --legacy-peer-deps finished, printed the v4 deprecation warning, and left a tree that npm ls rejects:

text
npm error code ELSPROBLEMS
npm error invalid: react@19.3.1 /w/legacy/node_modules/react

The runtime problem is worse. The React 19 upgrade guide removed ReactDOM.findDOMNode, which had been deprecated for years. Material UI v4 still calls it. In the installed @material-ui/core 4.12.4, ButtonBase, Modal, Popover, Tooltip, Menu and ClickAwayListener all reference it, and ButtonBase.js line 135 reads:

text
135:    return ReactDOM.findDOMNode(buttonRef.current);

With React 19.3.1 that function is gone (typeof findDOMNode printed undefined, against function on React 18.3.1). Mounting a single v4 Button with createRoot in jsdom failed straight away:

text
thrown: AggregateError [
  'TypeError: ReactDOM.findDOMNode is not a function',
  'TypeError: ReactDOM.findDOMNode is not a function'
]

On React 18.3.1, typeof findDOMNode printed function: the deprecated API is still there, so this particular call has something to reach (v4 was not mounted on React 18 in this test). Either way, the bypass runs an unmaintained library outside its declared range. The second Stack Overflow answer's other route, downgrading React to 17, does satisfy the range. It is only worth it for a legacy v4 codebase you cannot migrate yet, never for a new project.

Existing v4 code: migrate, do not just rename#

For a new project, the fix above is the whole job. For an app already written against v4, changing the package names is not enough: v5 changed the styling engine from JSS to Emotion, and several component defaults. MUI's migration guide gives the order. It covers updating React to at least 17, then running the preset-safe codemod once per folder:

bash
npx @mui/codemod@latest v5.0.0/preset-safe src

That codemod covers v4 to v5 only. The current release on npm is a later major, and the MUI documentation has a separate upgrade guide for each step after v5 (v6, v7 and v9; the registry has no stable 8.x release), so go one major at a time and run the app between steps.

When @mui/icons-material itself raises ERESOLVE#

A newer variant hits projects that already use the new names. The icons package declares a peer on @mui/material. If a project pinned to @mui/material v5 runs a bare npm install @mui/icons-material, npm fetches the latest icons and refuses:

text
npm error Found: @mui/material@5.18.0
npm error peer @mui/material@"^9.5.0" from @mui/icons-material@9.5.0

The cure is to install an icons release whose peer range covers the @mui/material you have, not to bypass the check. A major tag is not always enough: the icons manifest asks for ^9.5.0, so a project on an earlier 9.x would still be refused by the latest 9.x icons. The simplest rule is the same version number as your @mui/material. In the reproduction, the project was on the latest v5, so npm install @mui/icons-material@5 resolved to @mui/icons-material@5.18.0 alongside @mui/material@5.18.0 and exited 0. Read the Found line for the version you have and the peer line for the version the new package expects; the gap between them is the fix.

Once Material UI installs cleanly in a Next.js app, next build may then print __barrel_optimize__ lines that look like errors. The barrel_optimize warnings page explains where they come from and when they hide a real bundling problem. If npm on an old toolchain instead only warns that a peer "is not installed", that is a different, older behaviour, covered in the npm 3 to 6 missing peer page. In both cases, reading the package name and range in npm's message tells you more than any flag.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote