Quiz

What do you think of CommonJS vs ESM?

Topics
JavaScript

TL;DR

ECMAScript modules (ESM) are the JavaScript standard and the best default for new code intended for browsers, Node.js, or bundlers. Their static import / export structure provides live bindings and enables ahead-of-time dependency analysis; dynamic import() and top-level await cover asynchronous loading needs. CommonJS (CJS) is Node.js's older require() / module.exports system and remains important in existing packages and applications.

Interop is runtime- and version-specific. In current Node.js, ESM can import CommonJS, while require() can load only eligible synchronous ESM graphs without top-level await. Define the package format explicitly with file extensions, package.json type, and exports, then test the packed artifact. AMD is now mainly historical or a legacy-maintenance concern.


Overview

As JavaScript applications grow, organizing code into separate files becomes important. Module systems help manage this complexity by allowing code to be split into reusable pieces (modules) with clear dependencies. Over time, different module systems emerged before an official standard was adopted.

CommonJS

Characteristics

  • Designed for synchronous loading of modules
  • Primarily used in server-side environments like Node.js
  • Uses module.exports and require for defining and loading modules

Example

// Defining a module in CommonJS
const dep1 = require('dependency1');
const dep2 = require('dependency2');
module.exports = {
// Module code here
};
// Loading a module in CommonJS
const mod1 = require('module1');
const mod2 = require('module2');
// Code that uses mod1 and mod2

ESM (ECMAScript Modules)

Characteristics

  • Official JavaScript standard module system
  • Supported natively in modern browsers and Node.js
  • Designed for both asynchronous and synchronous scenarios
  • Uses export and import for defining and loading modules

Example

import dep1 from 'dependency1'; // if dependency1 had a default export
import { dep2 } from 'dependency2'; // named import to import something specific from dependency2
// Module code using dep1, dep2...
export function someFunction() {
// ... function logic ...
}
export const someValue = 'hello';
// Or export multiple things at once
// export { someFunction, someValue };
// Or export a default value
// export default class MyClass {
// ...
// }

Key differences

Loading mechanism

  • CommonJS: Synchronous (blocks until loaded).
  • ESM: Statically linked; browsers fetch module graphs asynchronously, and modules can use top-level await.

Environment suitability

  • CommonJS: Node.js legacy standard.
  • ESM: Official standard (Browser & Node.js).

Syntax

  • CommonJS: require() / module.exports.
  • ESM: import / export.

Analysis

  • CommonJS: Dynamic (runtime analysis).
  • ESM: Static (compile-time analysis, enables tree-shaking).

Use cases

CommonJS

  • Older Node.js projects or where sync loading is needed.
  • Existing CommonJS packages and applications.
  • Files or package scopes explicitly marked as CommonJS. Modern Node.js also performs syntax detection for some ambiguous input, so packages should set type rather than depend on ambiguity.

ESM

  • Modern web development (native browser support).
  • New Node.js projects, especially those needing async features or optimizations.
  • Code intended for both browser and server.

Practical interoperability notes

  • Relative ESM specifiers in browsers and Node.js use URL-style resolution and normally need explicit file extensions.
  • Importing a CommonJS module from ESM reliably exposes module.exports as the default export; named exports depend on Node.js static analysis and are not always available.
  • Current Node.js can synchronously require() an eligible ESM graph only when it has no top-level await. Use import() when the graph may be asynchronous or when supporting older Node.js versions.
  • Publishing separate ESM and CommonJS builds can create a “dual package” hazard where the same library is instantiated twice. Prefer one format when consumers allow it, or design and test conditional exports carefully.
  • ESM syntax enables tree-shaking analysis but does not guarantee unused code will be removed; side effects and bundler configuration still matter.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements correctly describe CommonJS and ESM in current JavaScript projects? Select all that apply.