The error, verbatim
Jump to the fix ↓Error [ERR_REQUIRE_ESM]: require() of ES Module ...\node_modules\chalk\source\index.js
from ...\app.js not supported.
Instead change the require of index.js in ...\app.js to a dynamic import()
which is available in all CommonJS modules.
code: 'ERR_REQUIRE_ESM'
Tested on
- Node
- 20.20.2, 22.11.0, 22.12.0, 24.22.1, 26.13.0 (Linux); 24.14.0, 26.11.1 (Windows)
- Package
- chalk 5.6.2 (ESM-only)
- TypeScript
- 6.0, compiled to CommonJS
- OS
- Ubuntu on GitHub Actions; Windows 11 Pro
✓ Last re-run on GitHub Actions · see the run · all repros
Contents
ERR_REQUIRE_ESM means CommonJS code (require) tried to load a package that only ships as an ES module. Many popular packages went ESM-only in a major version: chalk 5, node-fetch 3, got 12, nanoid 4 and more. Whether you get the error depends almost entirely on your Node version, so I ran the same code on five of them:
| Node | require() an ESM-only package |
require() an ES module with top-level await |
|---|---|---|
| 20.20.2 | ✅ loads | ❌ ERR_REQUIRE_ASYNC_MODULE |
| 22.11.0 | ❌ ERR_REQUIRE_ESM |
❌ ERR_REQUIRE_ESM |
| 22.12.0 | ✅ loads, with an ExperimentalWarning |
❌ ERR_REQUIRE_ASYNC_MODULE |
| 24.22.1 | ✅ loads | ❌ ERR_REQUIRE_ASYNC_MODULE |
| 26.13.0 | ✅ loads | ❌ ERR_REQUIRE_ASYNC_MODULE |
So on a current Node, plain require() of an ES module works, and ERR_REQUIRE_ESM mostly comes from Node 22.11 and older 22.x, or from older Node 20 and 18 releases, which I didn’t test here. Note the Node 20 row: the current 20.x release loads ES modules with require() too.
The fix
Option 1: use a dynamic import() from CommonJS. It works on every Node version, old or new:
(async () => {
const { default: chalk } = await import("chalk");
console.log(chalk.green("works"));
})();Option 2: upgrade Node. On 22.12 and newer, and on the current Node 20 release, the code that throws ERR_REQUIRE_ESM on 22.11 just runs. If you’re stuck on an old version anyway, this is the least-effort fix. After upgrading, read the next section: the package’s default export moves.
Option 3: pin the last CommonJS version of the package, if one exists (for chalk that’s chalk@4). Or convert your own project to ESM with "type": "module" and use import everywhere.
Upgraded Node and now see “is not a function”?
This is the trap after the upgrade. require() of an ES module returns the module namespace, so the package’s default export sits under .default. On every version where require("chalk") loaded, chalk.green was undefined:
TypeError: chalk.green is not a function
Add .default:
const chalk = require("chalk").default;
console.log(chalk.green("works"));
On Node 22.12 the load also printed a warning, which is harmless and gone in Node 24:
ExperimentalWarning: CommonJS module .../a.cjs is loading ES Module .../node_modules/chalk/source/index.js using require().
TypeScript compiled to CommonJS
A very common way into this error: your TypeScript says import chalk from "chalk", but tsc compiles it to require("chalk") because module is commonjs. The compiled file failed on Node 22.11.0 with the same error:
Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/chalk/source/index.js from .../out/g.cjs not supported.
On 20.20.2, 22.12.0, 24 and 26 the same compiled file ran, and with esModuleInterop on, chalk.green worked without adding .default: TypeScript’s interop helper picks the default export for you. So for a TypeScript project, upgrading Node is usually the whole fix.
The other new error: ERR_REQUIRE_ASYNC_MODULE
require() still can’t load an ES module that uses top-level await, on any version:
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.
Load that module with import() instead:
import("./tla/index.js").then((m) => console.log(m.default));If you wrote the top-level await in a .cjs file yourself, you get a different error, because CommonJS can’t contain it at all:
Warning: Failed to load the ES module: ...\f.cjs. Make sure to set "type": "module" in the nearest package.json file or use the .mjs extension.
SyntaxError: await is only valid in async functions and the top level bodies of modules
Wrap the code in an async function, or make the file an ES module (.mjs, or "type": "module").
Why it happens
Node has two module systems: CommonJS (require) and ES modules (import). For years require() refused ES modules outright, which is ERR_REQUIRE_ESM. Node 22.12 turned on require() of ES modules by default, and the current Node 20 release does it too, as long as the module graph has no top-level await. That’s why the same project fails on one machine and works on another: check node -v first.
How this was tested
October 31, 2026. The same folder (chalk 5.6.2, an ES module with top-level await, and a TypeScript file compiled to CommonJS with TypeScript 6 and esModuleInterop) ran on Node 20.20.2, 22.11.0, 22.12.0, 24.22.1 and 26.13.0 on GitHub Actions ubuntu-latest, and the top-level await cases on Node 24.14.0 and 26.11.1 on Windows 11 Pro. The first version of this post (September) simulated old Node with --no-experimental-require-module on Node 24; this version uses the real releases. The script and the full output are public.
— N.K., end of entry No.005