CodePark/ questions / Modules (CommonJS & ESM)
START PRACTICINGPRACTICE
Backend question · Node.js › Modules (CommonJS & ESM)

How does Node decide whether a file is CommonJS or an ES module, and how do the two interoperate?

Node looks at the file extension first, then at package.json:

  • .mjs files are always ESM (ES modules).
  • .cjs files are always CommonJS.
  • A .js file follows the nearest package.json. "type": "module" makes it ESM. "type": "commonjs", or no type field, makes it CommonJS.

Recent Node versions can also detect ESM syntax in a .js file when the type is not clear. Still, setting type explicitly is clearer.

How they work together:

  • ESM can import CommonJS. module.exports becomes the default export. Node also tries to detect named exports, but this is best-effort and does not always work.
  • CommonJS can use import() to load ESM, in every Node version.
  • Since Node 22.12 and 20.19, require() can also load an ES module. This works only if the module and its imports have no top-level await. Otherwise it throws ERR_REQUIRE_ASYNC_MODULE.

In ESM, relative imports need file extensions. Also, require, __dirname and __filename are not defined.

  • .mjs is ESM, .cjs is CommonJS, and .js follows the type field in package.json.
  • ESM can import CommonJS, and module.exports becomes the default export.
  • CommonJS can use import(). In recent Node, require() can load ESM that has no top-level await.
  • ESM needs explicit file extensions and has no require or __dirname.