A React build or runtime throws "require() of ES modules is not supported" when CommonJS code tries to load an ES module (for example node-fetch v3 or strip-ansi v7) because Node enforces module system compatibility and CRA/Next builds still emit require().
Fixes ERR_REQUIRE_ESM
// fetch-wrapper.cjs
const fetch = (...args) =>
import('node-fetch').then(({ default: fetch }) => fetch(...args));
module.exports = { fetch };
// usage
const { fetch } = require('./fetch-wrapper.cjs');
fetch(url).then((result) => result.json());// fetch-wrapper.cjsconst fetch = (...args) =>import('node-fetch').then(({ default: fetch }) => fetch(...args));module.exports = { fetch };// usageconst { fetch } = require('./fetch-wrapper.cjs');fetch(url).then((result) => result.json());Error [ERR_REQUIRE_ESM]: require() of ES Module not supported
Node.js keeps CommonJS (`require()`) and ES Modules (`import`) intentionally separate. When a package, like node-fetch v3 or strip-ansi v7, declares itself as ESM-only (via "type": "module" or a .mjs entry), Node refuses to load it with `require()` and raises ERR_REQUIRE_ESM. Bundlers such as Create React App, Next.js, or custom Webpack configs still emit CommonJS-style loading for server-side code, so the runtime hits this error as soon as an ESM-only dependency is pulled into a CommonJS loader. In React stacks, this often surfaces when dependencies are upgraded to ESM-only releases. Node-fetch switched to ESM in the 3.0.0 beta, while strip-ansi 7.0 made the same leap. Even if your source files are still CommonJS, transitive imports from your build or CLI tooling can end up inside a CommonJS context and trigger the error. Fixing it requires either adopting ES modules across the app, loading ESM dependencies asynchronously, or pinning to the latest CommonJS-friendly version.
Wrap the ESM dependency in a helper that uses import(), which works from CommonJS:
// fetch-wrapper.cjs
const fetch = (...args) =>
import('node-fetch').then(({ default: fetch }) => fetch(...args));
module.exports = { fetch };
// usage
const { fetch } = require('./fetch-wrapper.cjs');
fetch(url).then((result) => result.json());Node-fetch 3+ dropped CommonJS in favor of ES modules, so require('node-fetch') now throws ERR_REQUIRE_ESM. Use this wrapper to keep the rest of your code CommonJS while loading the module asynchronously.
If your React project can move to ESM, update package.json:
{
"type": "module"
}Then replace require() with import/ export and include extensions on relative imports:
import React from 'react';
import App from './App.js';
export default App;Update build scripts (Webpack, Babel, jest) to handle .js/.jsx/.mjs/.cjs and enable module: "ES2020", moduleResolution: "node" in tsconfig. Next.js and CRA also support ESM entry files for API routes once they export with export default.
Temporarily downgrade the dependency until you can migrate tooling:
npm install [email protected]
npm install [email protected]
# or, with yarn:
# yarn add node-fetch@^2.6.7 strip-ansi@^6.0.1Node-fetch v2 and strip-ansi v6 are still CommonJS. Use overrides or Yarn resolutions if the package is pulled in transitively:
{
"overrides": {
"strip-ansi": "6.0.1"
}
}This lets the build succeed while you plan a permanent fix.
If the project uses "type": "module" but builds still require CommonJS configs (Webpack, Jest), rename them to .cjs:
mv webpack.config.js webpack.config.cjs
mv jest.config.js jest.config.cjsThen update your package.json scripts:
{
"scripts": {
"build": "webpack --config webpack.config.cjs"
}
}Files ending with .cjs are always interpreted as CommonJS, letting tooling still call require() without hitting ERR_REQUIRE_ESM.
Configure Webpack or Vite to handle .mjs and conditional exports so it can load ESM packages cleanly:
// webpack.config.js
module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.cjs'],
conditionNames: ['require', 'import', 'default']
}
};If a package.json exposes both import and require entry points, the bundler must pick the right one. Webpack 5 and Vite do this automatically if you include .mjs in resolve extensions and avoid forcing mainFields to CommonJS-only entries.
Node 18+ now ships with the WHATWG fetch API, so React servers running on a modern Node version can drop node-fetch entirely. For other runtimes, packages such as Got, Axios, or isomorphic-fetch stay CommonJS. When migrating, be careful with dual-package distributions: importing the ESM entry in some places and the CJS entry elsewhere can create duplicate instances, which breaks checks like instanceof React.Component. Finally, tools like esm-hook or ts-node --esm can smooth the transition but add extra transpilation steps.