This error occurs when you attempt to use the CommonJS require() function to import an ES module (ESM) in your React project. Node.js and modern bundlers enforce strict separation between module systems, causing this incompatibility.
This error indicates a fundamental incompatibility between two JavaScript module systems: CommonJS and ES Modules (ESM). CommonJS uses require() for synchronous imports, while ES Modules use import statements for asynchronous imports. When a package is configured as an ES module (either by setting "type": "module" in package.json or by using .mjs file extensions), Node.js prevents it from being loaded with require(). This is because ES modules support features like top-level await that require asynchronous loading, which conflicts with CommonJS's synchronous require() function. In React applications, this typically happens when you're using a modern bundler configuration, importing newer versions of packages that have migrated to ESM-only, or when your project's package.json has module type mismatches. Many popular packages like node-fetch (v3+), chalk (v5+), and others have moved to ESM-only in their latest versions.
For code that needs to load ES modules, use dynamic import() instead of require():
// Before (CommonJS)
const fetch = require('node-fetch');
// After (Dynamic Import)
const fetch = async () => {
const module = await import('node-fetch');
return module.default;
};
// Usage
async function getData() {
const fetchModule = await import('node-fetch');
const response = await fetchModule.default('https://api.example.com');
return response.json();
}Dynamic imports return a Promise, so you'll need to handle them in async functions.
Update your package.json to use ES modules throughout your project:
{
"type": "module"
}Then convert all require() statements to import:
// Before (CommonJS)
const React = require('react');
const Component = require('./Component');
module.exports = MyComponent;
// After (ES Modules)
import React from 'react';
import Component from './Component.js';
export default MyComponent;Note: When using "type": "module", you must include file extensions in relative imports (.js, .jsx).
If you must use require(), downgrade to the last CommonJS version of the package:
# Example: node-fetch v2 is CommonJS, v3 is ESM-only
npm install [email protected]
# Example: chalk v4 is CommonJS, v5 is ESM-only
npm install [email protected]Check the package's changelog or npm page to find the last CommonJS-compatible version. This is a temporary workaround and should be followed by proper module system migration.
If your project uses "type": "module" but you need some CommonJS files, rename them to .cjs:
# Rename files
mv webpack.config.js webpack.config.cjs
mv jest.config.js jest.config.cjsFiles with .cjs extension are always treated as CommonJS, even when package.json has "type": "module". This is useful for configuration files that must use require().
Update any references to these files in your scripts:
{
"scripts": {
"build": "webpack --config webpack.config.cjs"
}
}Update your webpack configuration to handle both module types:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { modules: 'auto' }],
'@babel/preset-react'
]
}
}
}
]
},
resolve: {
extensions: ['.js', '.jsx', '.mjs', '.cjs']
}
};For Next.js projects, update next.config.js:
module.exports = {
webpack: (config) => {
config.resolve.extensionAlias = {
'.js': ['.ts', '.tsx', '.js', '.jsx', '.mjs']
};
return config;
}
};Module System Migration Strategy:
When migrating a large React codebase from CommonJS to ES modules, follow this incremental approach:
1. Audit Dependencies: Run npm list and check which packages are ESM-only. Use tools like are-you-es5 to identify dependencies.
2. Dual Package Hazard: Some packages publish both CommonJS and ESM versions. If you import the same package via both systems, you may get duplicate instances, causing issues with instance checks (e.g., instanceof). Always use one module system consistently per package.
3. TypeScript Configuration: Update tsconfig.json for proper ESM support:
{
"compilerOptions": {
"module": "ES2020",
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}4. Jest and Testing: Jest uses CommonJS by default. For ESM support, add to jest.config.js:
export default {
transform: {},
extensionsToTreatAsEsm: ['.jsx'],
moduleNameMapper: {
'^(\.{1,2}/.*)\.js$': '$1'
}
};5. Conditional Exports: Modern packages use conditional exports in package.json to provide both CommonJS and ESM versions. Understanding these can help debug import issues:
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}6. __dirname and __filename: These CommonJS globals are not available in ES modules. Use import.meta.url instead:
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);Performance Considerations:
While dynamic import() works, it has performance implications since imports become asynchronous. For critical path code, prefer static imports with proper module configuration rather than runtime dynamic imports.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite