Explain the differences between CommonJS modules and ES modules in JavaScript
TL;DR
In JavaScript, modules are reusable pieces of code that encapsulate functionality, making it easier to manage, maintain, and structure your applications. Modules allow you to break down your code into smaller, manageable parts, each with its own scope.
CommonJS is an older module system that was initially designed for server-side JavaScript development with Node.js. It uses the require() function to load modules and the module.exports or exports object to define the exports of a module.
// my-module.jsconst value = 42;module.exports = { value };// main.jsconst myModule = require('./my-module.js');console.log(myModule.value); // 42
ES Modules (ECMAScript Modules) are the standardized module system introduced in ES6 (ECMAScript 2015). They use the import and export statements to handle module dependencies.
// my-module.jsexport const value = 42;// main.jsimport { value } from './my-module.js';console.log(value); // 42
CommonJS vs ES modules
| Feature | CommonJS | ES modules |
|---|---|---|
| Module Syntax | require() for importing module.exports for exporting | import for importing export for exporting |
| Environment | Primarily used in Node.js for server-side development | Designed for both browser and server-side JavaScript (Node.js) |
| Loading and linking | require() is synchronous | Static imports are linked before evaluation; import() returns a promise, and top-level await can make evaluation asynchronous |
| Structure | require() calls can be conditional | Static import/export declarations are top-level; dynamic loading uses import() |
| File extensions | .js (default) | .mjs or .js (with type: "module" in package.json) |
| Browser support | Not natively supported in browsers | Natively supported in modern browsers |
| Optimization | Limited optimization due to dynamic nature | Allows for optimizations like tree-shaking due to static structure |
| Compatibility | Widely used in existing Node.js codebases and libraries | JavaScript standard supported by browsers and modern server runtimes |
Loading model at a glance
CommonJS discovers dependencies while code executes, whereas ES modules expose a static graph that hosts can link before evaluation.
This static structure enables native asynchronous loading and build-time analysis such as tree shaking, while CommonJS remains convenient in legacy Node.js code.
Modules in JavaScript
Modules in JavaScript are a way to organize and encapsulate code into reusable and maintainable units. They allow developers to break down their codebase into smaller, self-contained pieces, promoting code reuse, separation of concerns, and better organization. There are two main module systems in JavaScript: CommonJS and ES modules.
CommonJS
CommonJS is an older module system that was initially designed for server-side JavaScript development with Node.js. It uses the require() function to load modules and the module.exports or exports object to define the exports of a module.
- Syntax: Modules are included using
require()and exported usingmodule.exports. - Environment: Primarily used in Node.js.
- Execution:
require()returns synchronously. - Modules are loaded dynamically at runtime.
// my-module.jsconst value = 42;module.exports = { value };// main.jsconst myModule = require('./my-module.js');console.log(myModule.value); // 42
ES Modules
ES Modules (ECMAScript Modules) are the standardized module system introduced in ES6 (ECMAScript 2015). They use the import and export statements to handle module dependencies.
- Syntax: Modules are imported using
importand exported usingexport. - Environment: Can be used in both browser environments and Node.js (with certain configurations).
- Loading and execution: Static dependencies are discovered and linked before a module evaluates. Dynamic
import()is asynchronous, and top-levelawaitcan suspend evaluation. - Support: Introduced in ES2015, now widely supported in modern browsers and Node.js.
- Static
importandexportsyntax can be analyzed without executing the module, which enables tools such as tree shakers. It does not guarantee a faster runtime or a smaller bundle.
// my-module.jsexport const value = 42;// main.jsimport { value } from './my-module.js';console.log(value); // 42
Summary
ES modules are the language standard and are usually the best default for new cross-runtime code. CommonJS remains widely used in Node.js packages and existing applications. Interoperability is runtime-specific: current Node.js can synchronously require() an eligible ES module only when its graph contains no top-level await, while import() can load either system through Node's ES module loader.
Further reading
- JavaScript modules - MDN
- Node.js: ECMAScript modules
- Node.js: Loading ECMAScript modules using
require() - Modules, introduction - javascript.info
- Modules - Eloquent JavaScript
- CommonJS vs. ES modules in Node.js
- Understanding CommonJS vs. ES Modules in JavaScript