CommonJS vs ES Modules
1. What CommonJS and ES Modules are
| Module system | Where it came from | Key goal |
|---|---|---|
| CommonJS (CJS) | Node.js (2009) | Let server-side JavaScript use modules |
| ES Modules (ESM) | The ECMAScript standard (ES6, 2015) | Create a native module system for JS (browsers + Node.js) |
2. Syntax
CommonJS:
// math.js
const PI = 3.14;
function add(a, b) {
return a + b;
}
module.exports = { PI, add };
// main.js
const math = require('./math');
console.log(math.add(2, 3)); // 5Uses:
require()for importing modulesmodule.exports/exportsfor exporting modules
ES Modules:
// math.mjs
export const PI = 3.14;
export function add(a, b) {
return a + b;
}
// main.mjs
import { add } from './math.mjs';
console.log(add(2, 3)); // 5Uses:
import/export, keywords from the ES6 standard- Supported natively by the browser and Node.js (with the right setup)
3. Main differences
| # | Criterion | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|---|
| 1 | Syntax | require, module.exports | import, export |
| 2 | Module loading | Dynamic (at runtime) | Static (at parse time) |
| 3 | Import type | A copy of the value (by value) | A live binding (by reference) |
| 4 | Asynchrony | Synchronous loading | Asynchronous loading |
| 5 | Top-level await | no | yes |
| 6 | this context | this points to module.exports | this is undefined (strict mode) |
| 7 | Module caching | Yes (loaded once) | Yes, but with a different mechanism |
| 8 | Support in Node.js | The default (.js) | Requires "type": "module" or the .mjs extension |
| 9 | Compatibility | Older packages (npm, Express, etc.) | The newer ECMAScript standard |
| 10 | Use in the browser | no (Node.js only) | yes (in <script type="module">) |
4. Static vs dynamic loading
-
CommonJS: Modules load at runtime,
require()can be called anywhere:javascriptif (condition) { const utils = require('./utils'); } -
ES Modules: Imports are static, they must sit at the top of the file:
javascriptimport utils from './utils.js'; // cannot be inside an if
This lets ESM:
- do tree-shaking (dropping unused code);
- optimize bundling and execution.
5. Live bindings
ES Modules support live bindings: if a value in the module changes, the import sees the change too.
ESM:
// counter.mjs
export let count = 0;
export function increment() { count++; }
// main.mjs
import { count, increment } from './counter.mjs';
increment();
console.log(count); // 1 (updated)CJS:
// counter.js
let count = 0;
function increment() { count++; }
module.exports = { count, increment };
// main.js
const { count, increment } = require('./counter');
increment();
console.log(count); // 0 (a copied value)6. Support in Node.js
CommonJS: on by default
.js files are treated as CommonJS unless "type": "module" is set.
ES Modules: must be turned on explicitly
Two ways:
- In
package.json:
{
"type": "module"
}→ then every .js file is treated as ESM.
2. Or use the .mjs extension:
node main.mjs7. An example of mixed code
Importing CommonJS from ESM:
// ESM
import pkg from './math.cjs';
console.log(pkg.add(2, 3));Importing ESM from CommonJS:
// CJS
(async () => {
const { add } = await import('./math.mjs');
console.log(add(2, 3));
})();Note: from CJS, importing ESM is only possible asynchronously, via import().
8. this context and global variables
| Feature | CommonJS | ES Modules |
|---|---|---|
this | Points to module.exports | undefined |
| Global objects | __dirname, __filename, require, module, exports | absent (can be obtained via import.meta.url) |
9. Support in npm packages
- Older packages (
express,mongoose,chalk@4), CommonJS. - Newer ones (
chalk@5,node-fetch,axios@next), ES Modules.
Mixing the two often causes errors:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module10. A summary comparison table
| Characteristic | CommonJS | ES Modules |
|---|---|---|
| File format | .js | .mjs / .js with "type": "module" |
| Import | require() | import |
| Export | module.exports | export |
| Loading | Synchronous | Asynchronous |
| Binding type | Copy | Live binding |
this context | module.exports | undefined |
| Browsers | no | yes |
| Tree-shaking | no | yes |
| Top-level await | no | yes |
| Compatibility | Older packages | The newer standard |
In short:
CommonJS is the older, synchronous, Node.js-specific module system. ES Modules is the modern, standard, cross-platform format supported by both Node.js and browsers.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.