CommonJS versus ES Modules
Node's two module systems — require/module.exports (CommonJS) and import/export (ES modules) — and how Node decides which one a file uses.
require('./mod') / module.exports = {} — versus — import x from './mod.js'; export { x };
Purpose
CommonJS (require/module.exports) is Node’s original module system, still extremely common in
existing code and many packages. ES modules (import/export) are the modern, standardized
system, shared conceptually with the browser (recall import and
export).
How Node decides
// package.json
{ "type": "module" }
- A
.jsfile follows the nearestpackage.json’s"type"field:"module"means ES modules, its absence (or"commonjs") means CommonJS. .mjsalways forces ES modules;.cjsalways forces CommonJS — regardless of"type".
Behavior notes
- CommonJS
requireis synchronous and can be called conditionally, inside a function, or with a computed path — ES moduleimportstatements are static (fixed at parse time), though dynamicimport()(a function, returning a promise) covers the same conditional/computed use case. - CommonJS modules can
requirean ES module only via dynamicimport()(asynchronous); ES modules canimporta CommonJS module more directly, though only its default export is guaranteed (CommonJS has no concept of named exports natively — Node does some inference, which isn’t always reliable for every package). - A package can ship both formats via conditional exports in
package.json, letting consumersimportorrequireit and get an appropriate build either way.
Common mistakes
- Mixing
requireandimportin the same file — pick one system per file, matching what that file’s module type actually is. - Assuming every CommonJS package’s named exports will import cleanly into an ES module — verify, or fall back to a default import plus property access.
Runtime notes
Both are Node-specific concerns; a browser only ever runs ES modules (or plain, non-module scripts) — CommonJS has no browser equivalent.