This error occurs when CommonJS module syntax (require/module.exports) is used in a browser environment without proper bundling or transpilation. Browsers natively support ES modules but do not recognize the CommonJS "exports" variable.
The "ReferenceError: exports is not defined" error happens when JavaScript code uses CommonJS module syntax (like module.exports or exports) in an environment where that variable doesn't exist - typically a web browser. Browsers don't have native support for CommonJS modules, which are a Node.js standard. When TypeScript compiles to CommonJS format (module: "commonjs" in tsconfig.json) or when unbundled CommonJS code runs directly in the browser, the generated code references the "exports" variable that doesn't exist in the browser's global scope. This commonly occurs in React projects when TypeScript configuration targets CommonJS output, when webpack or bundler configuration is incorrect, or when trying to load Node.js libraries directly in the browser without proper transpilation. The error prevents your application from loading and typically appears immediately when the script is parsed.
If your React code runs in a browser, modify your tsconfig.json to output ES modules instead of CommonJS:
{
"compilerOptions": {
"target": "ES6",
"module": "ES6", // or "ESNext", not "commonjs"
"jsx": "react-jsx",
"esModuleInterop": true,
"skipLibCheck": true
}
}Remove or comment out "module": "commonjs" and ensure the target is ES6 or higher. Modern browsers support ES6 modules natively, and bundlers handle them correctly.
If you're loading JavaScript directly in HTML without a bundler, add the type="module" attribute:
<!-- Before -->
<script src="index.js"></script>
<!-- After -->
<script type="module" src="index.js"></script>This tells the browser to treat the script as an ES module, which supports import and export statements. Note that module scripts are deferred by default.
If you're using Create React App, this should already be configured. For custom webpack setups, ensure your webpack.config.js targets the browser:
module.exports = {
target: 'web', // Not 'node'
output: {
libraryTarget: 'umd', // or 'var', 'window', depending on needs
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
};For Vite projects, ensure vite.config.ts has build.target set appropriately (e.g., 'es2015' or 'esnext').
If you have a custom Babel configuration, ensure presets don't transform ES modules to CommonJS for browser builds:
{
"presets": [
[
"@babel/preset-env",
{
"modules": false, // Preserve ES modules
"targets": {
"browsers": [">0.2%", "not dead"]
}
}
],
"@babel/preset-react",
"@babel/preset-typescript"
]
}Setting "modules": false prevents Babel from converting import/export to require/module.exports.
If your package.json has "type": "module", ensure TypeScript outputs ES modules:
// package.json
{
"type": "module"
}// tsconfig.json
{
"compilerOptions": {
"module": "ESNext", // Must use ES modules, not commonjs
"target": "ESNext"
}
}Mismatched settings cause TypeScript to emit CommonJS while Node expects ES modules (or vice versa).
Module System Deep Dive: The error fundamentally stems from the incompatibility between CommonJS (Node.js's original module system) and browser JavaScript. When TypeScript compiles with "module": "commonjs", it generates code like Object.defineProperty(exports, "__esModule", { value: true }); at the top of files. In Node.js, the exports variable is provided by the runtime, but browsers have no such concept.
Workaround for Legacy Code: If you absolutely must load CommonJS code in a browser without a bundler, you can add <script>var exports = {}; var module = { exports: {} };</script> before your script tags. However, this is a hack and won't handle inter-module dependencies correctly - use a proper bundler instead.
Create React App Note: CRA handles this automatically through react-scripts. If you see this error in a CRA project, it's usually from ejecting and then misconfiguring webpack/Babel, or from trying to import Node.js-only libraries. Check node_modules/.cache for corrupted build artifacts and try npm run build again after clearing cache.
Vite vs Webpack: Vite assumes ES modules by default and has faster dev builds because it leverages native browser ESM support. Webpack can bundle either way but requires explicit configuration. For new React projects, Vite's defaults align better with modern browser expectations.
TypeScript Isolate Modules: If using "isolatedModules": true (required for some transpilers), TypeScript will error on constructs that don't transpile correctly without type information, helping catch module system issues earlier.
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