Skip to main content

CommonJS vs ES Modules

1. What CommonJS and ES Modules are

Module systemWhere it came fromKey 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:

javascript
// 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)); // 5

Uses:

  • require() for importing modules
  • module.exports / exports for exporting modules

ES Modules:

javascript
// 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)); // 5

Uses:

  • import / export, keywords from the ES6 standard
  • Supported natively by the browser and Node.js (with the right setup)

3. Main differences

#CriterionCommonJS (CJS)ES Modules (ESM)
1Syntaxrequire, module.exportsimport, export
2Module loadingDynamic (at runtime)Static (at parse time)
3Import typeA copy of the value (by value)A live binding (by reference)
4AsynchronySynchronous loadingAsynchronous loading
5Top-level awaitnoyes
6this contextthis points to module.exportsthis is undefined (strict mode)
7Module cachingYes (loaded once)Yes, but with a different mechanism
8Support in Node.jsThe default (.js)Requires "type": "module" or the .mjs extension
9CompatibilityOlder packages (npm, Express, etc.)The newer ECMAScript standard
10Use in the browserno (Node.js only)yes (in <script type="module">)

4. Static vs dynamic loading

  • CommonJS: Modules load at runtime, require() can be called anywhere:

    javascript
    if (condition) { const utils = require('./utils'); }
  • ES Modules: Imports are static, they must sit at the top of the file:

    javascript
    import 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:

javascript
// 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:

javascript
// 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:

  1. In package.json:
javascript
{ "type": "module" }

→ then every .js file is treated as ESM. 2. Or use the .mjs extension:

javascript
node main.mjs

7. An example of mixed code

Importing CommonJS from ESM:

javascript
// ESM import pkg from './math.cjs'; console.log(pkg.add(2, 3));

Importing ESM from CommonJS:

javascript
// 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

FeatureCommonJSES Modules
thisPoints to module.exportsundefined
Global objects__dirname, __filename, require, module, exportsabsent (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:

javascript
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module

10. A summary comparison table

CharacteristicCommonJSES Modules
File format.js.mjs / .js with "type": "module"
Importrequire()import
Exportmodule.exportsexport
LoadingSynchronousAsynchronous
Binding typeCopyLive binding
this contextmodule.exportsundefined
Browsersnoyes
Tree-shakingnoyes
Top-level awaitnoyes
CompatibilityOlder packagesThe 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.