Topics in this subject
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 this is undefined.
  • 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):

  1. ESM vs CommonJS — three differences.
  2. What is tree shaking and why does ESM enable it?
  3. When would you use dynamic import()?