1
How it works
The explanation, with examples you can run.
Side by side
| ES modules | CommonJS | |
|---|---|---|
| Syntax | import / export | require() / module.exports |
| When it is resolved | Before any code runs (static) | When the require line runs |
| Loading | Can be asynchronous; allows top-level await | Synchronous |
| Exports | Live bindings: importers see later changes | A plain object, copied out at require time if you destructure |
| Strict mode | Always | Only with "use strict" |
| File location | import.meta.url, import.meta.dirname | __filename, __dirname |
Mixing them in Node
- An ES module can
importa CommonJS file. Itsmodule.exportsbecomes the default export, and Node tries to detect named exports too. - A CommonJS file can load an ES module with
await import(), and since Node 22.12 also with plainrequire(), as long as that module (and its imports) use no top-levelawait. - In ES modules, relative imports need the full file name:
import "./util.js", not"./util".
// math.mjsexport let calls = 0export function add(a, b) { calls++ return a + b} // main.mjs (run with: node main.mjs)import { add, calls } from "./math.mjs"add(1, 2)console.log(calls) // 1: the binding is live, not a copy2
Check yourself
2 questions. Get them all right to finish this topic.
0 of 2 right so far. Get all two to finish this topic.