I was working on a Next.js + TypeScript project when I ran into a strange and frustrating error during build:
Module not found: Can't resolve 'react/jsx-runtime'
At first, I was confused I already had react and react-dom installed. So why was Next.js acting like React didn’t exist.
Turns out, the problem wasn’t React itself but how it was being resolved under the hood. I’ll walk you through:
My Original Setup
Here’s my original package.json:
{
"name": "frontend",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "dotenv -e ../../.env -- sh -c 'next dev --port $FE_SERVER_PORT'",
"build": "next build"
},
"dependencies": {
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@types/react": "^19.1.11",
"@types/react-dom": "^19.1.8"
},
"overrides": {
"react": "$react"
}
}
And here’s my tsconfig.json:
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "react",
"module": "commonjs",
"target": "ES2023",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}
}
Everything looked fine but the problem was hiding in plain sight.
Why This Error Happens
Here’s what I learned:
- React 17 and above introduced a new internal package called
react/jsx-runtime
, which Next.js relies on for compiling JSX/TSX. - If Webpack (used internally by Next.js) can’t resolve this file, it assumes React is missing, even when installed.
- In my case, the issue came from two major suspects:
Cause | Explanation |
---|---|
Wrong React version | React 19 is still experimental in some Next.js setups |
Alias overrides | My "overrides" and Webpack aliasing unintentionally blocked proper module resolution |
How I Fixed It
Downgraded to Stable React 18
npm install react@18 react-dom@18
Removed Overrides in package.json
"overrides": {
- "react": "$react"
}
Cleaned Up Aliases (in next.config.ts
)
If you’ve added aliases like this remove them unless absolutely necessary:
config.resolve.alias = {
...config.resolve.alias,
- 'react': require.resolve('react'),
- 'react-dom': require.resolve('react-dom')
}
Let Next.js handle resolving React automatically — it’s smarter than we think!
Clean Install
rm -rf node_modules .next package-lock.json
npm install
npm run dev
At this point no more jsx-runtime error!
Practice Component to Confirm the Fix
To be sure everything was working, I wrote a tiny test component:
// components/Hello.tsx
import React from "react";
export default function Hello({ name }: { name: string }) {
return <h1>Hello, {name}! </h1>;
}
And used it inside my homepage:
// pages/index.tsx
import Hello from "@/components/Hello";
export default function Home() {
return (
<main>
<Hello name="Next.js Developer" />
</main>
);
}
If this renders correctly in the browser you’re officially free from the jsx-runtime nightmare.
Final Thought
This error was a great reminder that most React and Next.js build issues aren’t caused by missing packages they’re caused by misconfigurations. In my case, a couple of unnecessary overrides completely broke module resolution. Once I cleaned things up and let Next.js handle React the way it was designed to, everything just worked. Sometimes the best fix is not adding more it’s removing what isn’t needed.