What is the difference between CommonJS and ES Modules?
Two module systems for sharing code between files.
CommonJS — Node's original system: require() to import, module.exports to export. Synchronous, loaded at runtime.
ES Modules — the standard: import / export. Static, asynchronous, works in browsers and modern Node.
Static analysis — ESM's fixed structure enables tree shaking; CommonJS is dynamic.
Direction of travel: ES Modules are the modern standard; CommonJS lingers in older Node code.
// CommonJS
const fs = require('fs'); // Import
const { readFile } = require('fs'); // Destructured import
module.exports = { add, subtract }; // Export
module.exports = MyClass; // Default export
// ES Modules
import fs from 'fs'; // Default import
import { readFile } from 'fs'; // Named import
import * as fs from 'fs'; // Namespace import
export function add(a, b) { return a + b; } // Named export
export default class MyClass {} // Default export
// Dynamic import (ESM — like require but async)
const module = await import('./module.js');
// Interop
// ESM can import CJS (usually works)
// CJS cannot require() ESM (must use dynamic import())CJS uses require (sync) and module.exports. ESM uses import/export (async, static). Dynamic import() loads modules on demand (works in both systems).
ESM enables tree shaking because imports are statically analyzable. CJS cannot require ESM — use await import() instead.
CJS = require/module.exports (sync, dynamic, Node.js). ESM = import/export (async, static, standard). ESM enables tree shaking.
Know the interop: ESM can import CJS, CJS must use dynamic import() for ESM. ESM is the recommended future.