JavaScript
2 min read
Updated 4 Aug 2026
Part 18 — Modules
CommonJS (Node legacy) 🟡, ES Modules (ESM) 🟢, Dynamic import, ESM vs CommonJS 🎯
CommonJS (Node legacy) 🟡#
// export
const add = (a, b) => a + b;
module.exports = { add };
// import
const { add } = require("./math");
Synchronous, dynamic, runtime resolution — the historic Node system.
ES Modules (ESM) 🟢#
// named exports
export const PI = 3.14159;
export function area(r) { return PI * r * r; }
// default export
export default class Circle {}
// re-export
export { thing } from "./other.js";
// imports
import Circle, { PI, area } from "./circle.js";
import * as circle from "./circle.js";
import "./side-effect.js"; // run for effects only
- Static (imports are hoisted, resolved before execution) → enables tree shaking (bundlers drop unused exports).
- Always strict mode; top-level
thisisundefined. - Enable in Node via
"type":"module"in package.json or.mjs; in browsers via<script type="module">.
Dynamic import#
const { heavy } = await import("./heavy.js"); // load on demand → code splitting
button.addEventListener("click", async () => {
const mod = await import("./chart.js"); // lazy-load a feature
mod.render();
});
Returns a promise; enables lazy loading, conditional loading, and route-based code splitting.
ESM vs CommonJS 🎯#
| CommonJS | ESM | |
|---|---|---|
| Syntax | require/module.exports |
import/export |
| Loading | synchronous, runtime | static, hoisted |
| Tree-shakeable | no | yes |
Top-level await |
no | yes |
| Live bindings | copies values | live references |
Interview questions (Part 18):
- ESM vs CommonJS — three differences.
- What is tree shaking and why does ESM enable it?
- When would you use dynamic
import()?