ESM vs CommonJS
ESM vs CommonJS
Section titled “ESM vs CommonJS”Introduction
Section titled “Introduction”ES Modules (ESM) is JavaScript’s official module system. CommonJS is the module system used by Node.js before ESM. Understanding the differences is important for working with both environments.
Key Differences
Section titled “Key Differences”| Feature | ESM | CommonJS |
|---|---|---|
| Syntax | import / export | require() / module.exports |
| Loading | Static (parse time) | Dynamic (runtime) |
| Scope | Module scope | Module scope |
| Strict mode | Always | Optional |
this at top level | undefined | exports object |
| Top-level await | ✅ Yes | ❌ No |
| File extensions | Required (.js, .mjs) | Optional |
Syntax Comparison
Section titled “Syntax Comparison”// ESM — exportexport const greet = () => 'Hello';export default class User {}
// CommonJS — exportmodule.exports.greet = () => 'Hello';module.exports = class User {};
// ESM — importimport User, { greet } from './user.js';
// CommonJS — importconst User = require('./user');const { greet } = require('./user');Static vs Dynamic
Section titled “Static vs Dynamic”// ESM — static (analyzed at parse time)import { something } from './module.js'; // must be at top level
// CommonJS — dynamic (can be conditional)if (condition) { const module = require('./module.js'); // ✅ works}
// ESM — dynamic import for runtime loadingif (condition) { const module = await import('./module.js'); // ✅}Circular Dependencies
Section titled “Circular Dependencies”CommonJS handles circular dependencies by returning the partially built module.exports. ESM handles them better due to live bindings:
// ESM: imports are live read-only bindings to the exporting module// Even with circular deps, you always get the latest value
// CommonJS: imports are copies at require time// Circular deps can get undefined if accessed too earlyFile Extensions
Section titled “File Extensions”// ESM — must include extension (in browsers, Node.js)import { greet } from './utils.js'; // ✅import { greet } from './utils'; // ❌ in browsers
// CommonJS — extension optionalconst { greet } = require('./utils'); // ✅const { greet } = require('./utils.js'); // ✅Using Both
Section titled “Using Both”// Package.json allows mixing with "type" field{ "type": "module", // .js files treated as ESM "exports": { "import": "./src/index.js", // ESM consumers "require": "./dist/index.js" // CommonJS consumers }}Summary
Section titled “Summary”| Aspect | ESM | CommonJS |
|---|---|---|
| Standard | Official JavaScript spec | Node.js convention |
| Syntax | import / export | require / module.exports |
| Loading | Static (faster) | Dynamic (flexible) |
| Top-level await | ✅ | ❌ |
| Browser support | ✅ Native | ❌ (needs bundler) |
| File extension | Required | Optional |