ReferenceError: require is not defined, explained and fixed

This is the mirror image of the import error: code written for CommonJS, using require() and module.exports, is running in an environment that only provides ES modules. That happens in two places, a browser (which has never had a global require outside bundlers) and a Node package whose package.json says "type": "module", where .js files are ES modules and require is not created for them.

The fixes, by situation

  1. In Node ESM, convert the calls: const fs = require('fs') becomes import fs from 'fs'. Most built-ins and modern packages support this directly, and it is the clean long-term answer.
  2. When a specific CommonJS-only dependency must be loaded synchronously, recreate require legitimately: import { createRequire } from 'module'; const require = createRequire(import.meta.url); then use it for that dependency.
  3. To keep one file CommonJS inside an ESM package, rename it to .cjs. Node always treats .cjs as CommonJS, with a working require, regardless of the package type.
  4. In the browser, there is no file-level fix: the code must be bundled (webpack, Vite, esbuild turn require into bundled modules) or rewritten as native ES modules with <script type="module">. Pasting Node-style code into a plain page cannot work as-is.

Details that bite during conversion

  1. __dirname disappears along with require in ES modules; derive it from import.meta.url when a converted file used it for paths.
  2. Dynamic require(variable) has no direct import equivalent; static imports are analyzed at load time. Use await import(variable) (asynchronous) or an explicit map of the possible modules.
  3. Some packages expose different builds per system: a library that worked through require may ship an ESM entry with slightly different exports, check its documentation when a named import is reported missing after conversion.

Common questions

Why did require stop working after I edited package.json? Adding "type": "module" reclassifies every .js file in the package as an ES module. Files still written in CommonJS style then fail on require, __dirname, and module.exports until converted or renamed to .cjs.

Is createRequire a hack? No, it is the standard library's official bridge for loading CommonJS from an ES module, intended exactly for dependencies without an ESM entry. Prefer plain imports where they exist.

My config file uses require, why does the tool complain? Many tools load their config (for example .config.js files) as ES modules when the package is type module. Rename the config to .cjs or convert it to an export default, both are accepted by most tools.

Related errors

Cannot use import statement outside a module · Node: Cannot find module · ReferenceError: is not defined