CommonJS vs ES Modules: Key Differences
JavaScript utilizes two main module systems for organizing and reusing code: CommonJS (CJS) and ECMAScript Modules (ES Modules or ESM). While CommonJS was built primarily for server-side execution in original Node.js environments using synchronous loading, ES Modules serve as the official ECMAScript standard designed for both modern browsers and server-side runtimes with static, asynchronous loading. Understanding the differences in syntax, loading behavior, performance optimizations, and execution context is essential for modern JavaScript development.
1. Syntax Comparison
The most noticeable difference between the two systems is the syntax used to export and import modules.
CommonJS (CJS)
CommonJS uses require() to import dependencies and
module.exports or exports to share values:
// Exporting in CommonJS (math.js)
const add = (a, b) => a + b;
module.exports = { add };
// Importing in CommonJS (app.js)
const { add } = require('./math.js');
console.log(add(2, 3));ES Modules (ESM)
ES Modules use the import and export
statements:
// Exporting in ESM (math.js)
export const add = (a, b) => a + b;
// Importing in ESM (app.js)
import { add } from './math.js';
console.log(add(2, 3));2. Loading Mechanism: Synchronous vs. Asynchronous
- CommonJS is synchronous and dynamic: Modules are
loaded and executed sequentially at runtime when the
require()function is called. Becauserequire()is a standard function, it can be called conditionally insideifstatements or functions. - ES Modules are asynchronous and static: The
structure of an ES Module (
import/export) must be declared at the top level of the file. The JavaScript engine parses the module dependency graph before executing any code. Dynamic imports are supported via the asynchronousimport()function.
3. Static Analysis and Tree Shaking
Because ES Modules are statically analyzed during the compilation phase: * Tree Shaking: Bundlers (such as Webpack, Rollup, or Vite) can detect unused exports and eliminate dead code from the final bundle. * Early Error Detection: Syntax errors, missing exports, and cyclic dependency issues can often be caught before runtime.
CommonJS modules cannot be easily optimized with tree shaking because imports are resolved dynamically during code execution.
4. Default Environment and File Extensions
- Node.js Defaults: Historically, Node.js treated all
.jsfiles as CommonJS. To use ES Modules in Node.js, you must either set"type": "module"inpackage.jsonor use the.mjsfile extension. CommonJS files in an ESM package use the.cjsextension. - Browsers: Modern web browsers natively support ES
Modules using
<script type="module" src="app.js"></script>. Browsers do not natively support CommonJS without a bundler.
5. Scope and Built-in Variables
ES Modules and CommonJS treat lexical context differently:
| Feature | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
Top-Level
this |
Points to module.exports |
undefined |
__dirname &
__filename |
Available natively | Not available (use
import.meta.url) |
Top-Level
await |
Not supported | Supported |
| Strict Mode | Optional ("use strict";) |
Enabled by default |
Summary
CommonJS remains relevant for legacy Node.js projects and existing npm packages, but ES Modules are the official standard for modern JavaScript development across both client-side and server-side environments.