JavaScript modules let you split code into reusable files with explicit imports and exports. The two systems are ES Modules (ESM, the standard) and CommonJS (CJS, Node's original system).
ESM uses import/export, is statically analyzable (enabling tree-shaking), loads asynchronously, and is the browser and modern standard (files often use .mjs or "type": "module"). CommonJS uses require() and module.exports, loads synchronously, and is Node's historical default (.cjs or default .js). Key differences: ESM imports are live read-only bindings resolved at load time; CJS require returns a copied value at call time and can be called conditionally. Mixing them causes friction — this is exactly the ERR_REQUIRE_ESM error you hit when require() is used on an ESM-only package. Modern tooling (and Node 22+) increasingly bridges the two, but knowing which system a package uses matters for imports.
A test project's prerender script did const puppeteer = require('puppeteer') and broke on CI because modern Puppeteer is ESM-only — require() of an ES module throws ERR_REQUIRE_ESM. The fix was dynamic import: const puppeteer = (await import('puppeteer')).default.
Your CommonJS script needs to use a package that only ships as an ES Module. require('the-package') throws ERR_REQUIRE_ESM. How do you load it?