nodeseverity: blocker
ERR_REQUIRE_ESM

Node.js ERR_REQUIRE_ESM: require() of ES Module is not supported

require() of ES Module not supported

95% fixable~10 mindifficulty: intermediate

Verified against Node.js docs: Modules: ECMAScript modules, Node.js docs: Errors, Node 22 release notes: --experimental-require-module · Updated May 2026

> quick_fix

A CommonJS module called require() on an ESM-only package. Two fixes: (1) convert your file to ESM by setting type: module in package.json and using import; (2) use dynamic import() inside your CJS code: const pkg = await import('esm-only').then(m => m.default).

// Bad - CJS require() of ESM
const chalk = require('chalk')  // ERR_REQUIRE_ESM (chalk 5+ is ESM-only)

// Fix 1: dynamic import in CJS
async function main() {
  const { default: chalk } = await import('chalk')
  console.log(chalk.green('hi'))
}

// Fix 2: convert your file to ESM
// package.json: "type": "module"
import chalk from 'chalk'

What causes this error

Node distinguishes ESM (.mjs, or .js with type: module) from CommonJS (.cjs, or .js with type: commonjs). require() is CJS-only and synchronous; ESM uses import which is asynchronous. When require() encounters a module that has "type": "module" in its package.json or .mjs extension, Node throws ERR_REQUIRE_ESM because there's no synchronous way to load ESM. Many modern packages (chalk 5+, node-fetch 3+, ora 6+, p-limit 4+) are ESM-only.

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Identify whether your file is CJS or ESM

    Check the nearest package.json: type: 'module' makes .js files ESM; type: 'commonjs' (default) makes them CJS. .mjs is always ESM, .cjs is always CJS regardless of type field.

    # Read your package.json
    node -e "console.log(require('./package.json').type ?? 'commonjs')"
    
    # Check the file extension
    ls *.js *.mjs *.cjs
  2. 02

    step 2

    Confirm the offending package is ESM-only

    Look at the package's package.json type field. ESM-only packages have type: 'module' and no 'exports' map providing a CJS entry. The package's README usually says 'ESM only' or 'pure ESM'.

    cat node_modules/chalk/package.json | grep type
    # "type": "module"
  3. 03

    step 3

    Use dynamic import() in CJS code

    Dynamic import works from both CJS and ESM. The result is a promise. This is the simplest fix when you can't migrate the entire file.

    // CJS file
    async function main() {
      const { default: chalk } = await import('chalk')
      console.log(chalk.green('OK'))
    }
    main()
  4. 04

    step 4

    Or migrate your file to ESM

    Add "type": "module" to package.json or rename to .mjs. Then use import. Note: this affects all .js files in that package - be ready to update every require() and __dirname reference.

    // package.json
    {
      "type": "module"
    }
    
    // Then your file uses import
    import chalk from 'chalk'
    // And __dirname becomes:
    import { fileURLToPath } from 'node:url'
    const __dirname = path.dirname(fileURLToPath(import.meta.url))
  5. 05

    step 5

    Pin to a CJS-compatible version

    If migration is too disruptive, find the last version of the package that supported CJS. chalk 4 still works; node-fetch 2 still works. Note: those versions get no security updates, so this is short-term only.

    pnpm add chalk@4
    pnpm add node-fetch@2
  6. 06

    step 6

    Use a build tool that bundles ESM into CJS

    tsup, esbuild, and rollup can bundle an ESM dependency into a CJS-compatible output. For libraries shipping CJS, this lets you depend on ESM packages while still publishing CJS.

    # tsup config (tsup.config.ts)
    export default {
      entry: ['src/index.ts'],
      format: ['cjs', 'esm'],
      noExternal: ['esm-only-package'],
    }

Why ERR_REQUIRE_ESM happens at the runtime level

Node's CJS loader (Module._load in lib/internal/modules/cjs/loader.js) calls fs.readFileSync to read a module synchronously, then evaluates it in a CJS wrapper. When the resolved module has type: module in its package.json or a .mjs extension, the loader detects this in resolveExports and throws ERR_REQUIRE_ESM rather than attempting evaluation. The fundamental issue is that ESM evaluation is asynchronous (top-level await is allowed) and CJS require is synchronous; there's no way to make the synchronous call wait for an asynchronous module graph to resolve. Node 22's --experimental-require-module relaxes this for modules without top-level await.

Common debug mistakes for ERR_REQUIRE_ESM

  • Upgrading chalk, node-fetch, or similar package to a major version without checking ESM-only status.
  • Mixing CJS and ESM in the same project without a clear convention for which extension/file handles which.
  • Using __dirname or require.resolve in ESM files; these are CJS-only and need import.meta.url replacements.
  • Bundling for CJS but pulling in ESM-only deps; the bundle works locally but consumers see ERR_REQUIRE_ESM.
  • Trying to await an import at the top of a CJS file - that requires top-level await, which CJS doesn't support.

When ERR_REQUIRE_ESM signals a deeper problem

Recurring ERR_REQUIRE_ESM in a Node project signals incomplete migration to ESM. The architectural fix is to commit to one module system: convert all .js to ESM by setting type: 'module', or pin every dependency to its last CJS-compatible version. Mixed CJS/ESM codebases accumulate friction; every new dependency becomes a compatibility audit. For libraries published to npm, dual-publishing CJS+ESM via tsup is the practical answer. For applications, going pure ESM is cleaner long-term. The pain of migration is bounded; the cost of half-migrating compounds with every new dependency upgrade.

Frequently asked questions

Why are so many packages going ESM-only?

ESM is the JavaScript standard module system. CJS is Node-specific. Maintainers of pure-JS libraries see ESM as the future and drop CJS to simplify maintenance, enable tree-shaking better, and align with browser-side conventions. Sindre Sorhus (chalk, ora, p-limit author) wrote a manifesto explaining the move and many followed. The downside is friction for users who haven't migrated. Node 22 added top-level await in CJS via dynamic import, easing some of this pain. Long-term, every Node project will be ESM.

Is dynamic import() always the right fix?

Often, but not always. Dynamic import is asynchronous, so anywhere your CJS code expects a synchronous require result, you'll have to refactor. Module-load-time globals (declaring a chalk instance at top level) become awaitable, which cascades through your code. If a single file uses many ESM packages, migrating that file to ESM is usually less work than scattering await import everywhere. Decide based on how deeply the dependency is woven through the file.

What about TypeScript - does ts-node handle this?

ts-node supports both CJS and ESM modes but you must opt in. With CommonJS, ESM-only deps still throw ERR_REQUIRE_ESM. To handle ESM, run with: node --loader ts-node/esm script.ts and set type: module in package.json. Or use tsx (ts-node alternative) which handles ESM out of the box. For Next.js, Vite, and similar bundler-driven projects, this is invisible because the bundler handles module conversion. Plain Node + TypeScript is where the friction shows up.

Will Node ever allow require() of ESM?

Partially yes. Node 22 added experimental --experimental-require-module flag that allows synchronous require() of ESM if the ESM module has no top-level await. This works because such modules behave equivalent to CJS at execution time. Node 23 promotes this to default behavior. So in the long run, the error becomes rarer - though packages that use top-level await will continue to be require-incompatible. Until then, ERR_REQUIRE_ESM remains the most common Node module-system error.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is manually verified against official sources listed in the “sources” sidebar. If a fix here didn’t work for you, please email so we can update the page.