ES Modules VS CommonJS
on this page
Introduction
The JavaScript ecosystem has evolved significantly over the years, and one of the pivotal changes has been the introduction of ES Modules (ESM) alongside CommonJS (CJS). They are basically module systems, which allow you to split your code base into several files(along with other benefits). These two module systems serve as essential tools for JavaScript developers, but they differ in several ways. In this article, we'll delve into the key distinctions between ESM and CJS, explore their import mechanisms, discuss their asynchronous nature, and consider the implications for package maintainers.
Difference between ESM, and CJS
Usage
"type" Field in package.json:
The "type"
field in the package.json
file plays a crucial role in determining the default module system for your project. Understanding and configuring this field can greatly impact the usability of ESM and CJS.
- Type
"module"
:- When you set
"type": "module"
in yourpackage.json
, you indicate that your project primarily uses ES Modules (ESM). - Files with the
.js
extension are treated as ESM modules by default. - Example configuration in
package.json
:
- When you set
{
"type": "module",
// ...
}
- Type
"common"
:- Setting
"type": "common"
or not specifying the"type"
field at all in yourpackage.json
implies that your project primarily uses CommonJS (CJS). - Files with the
.js
extension are treated as CommonJS modules by default. - Example configuration in
package.json
:
- Setting
{
"type": "common",
// ...
}
File Extensions: .mjs and .cjs:
.mjs
Extension:-
Files with the
.mjs
extension are explicitly treated as ES Modules. -
Using
.mjs
can make it clear that a file uses ESM syntax and behavior. -
Example usage:
// app.mjs (ESM) import { someFunction } from './module.mjs';
-
.cjs
Extension:-
Files with the
.cjs
extension are explicitly treated as CommonJS modules. -
Using
.cjs
can indicate that a file follows CommonJS syntax and conventions. -
Example usage:
// app.cjs (CommonJS) const someFunction = require('./module.cjs');
-
Syntax and Usage
- ESM: ES Modules follow the import and export syntax, allowing developers to create modules that can be imported and used in other parts of their code.
- CJS: CommonJS, on the other hand, uses the
require
function to load modules and themodule.exports
orexports
object to define what should be exposed.
Dynamic vs Static Imports
- ESM: ESM uses static imports, which means that the imported modules are resolved and loaded during the initial parsing of the code. However, ESM also supports dynamic imports through the asynchronous
import()
function, allowing for asynchronous loading of modules at runtime when needed. - CJS: CommonJS primarily uses synchronous (static) imports when you use the require() function to load modules. While there is no direct built-in support for dynamic imports in CommonJS, some tools and environments may provide workarounds or extensions to achieve similar functionality.
Interoperability
Usually you wouldn't need to worry about this, as most package maintainers provide ESM, and CJS wrappers for their package, but there are some packages that provide only one. So it is important to know how you could use either in your project.
ESM in CJS
- ESM modules can be utilized within a CommonJS environment, but the process can feel unconventional.
- To import ESM modules in a CommonJS project, you can use the
import()
function, which allows asynchronous loading of ESM modules. - For example:
// CommonJS module (myCJSModule.js)
async function loadESMModule() {
const { greet } = await import('./myESMModule.mjs');
console.log(greet()); // Output: Hello, world!
}
loadESMModule();
CJS in ESM
- ES Modules can natively import CommonJS modules without special configuration.
- To use CommonJS modules within ES Modules, you can use the
import * as
syntax to import the entire CommonJS module. - For example
// ES Module (myESMModule.mjs)
import * as myCJSModule from './myCJSModule.js';
console.log(myCJSModule.greet()); // Output: Hello, world!
Final Note
- JavaScript's module landscape comprises two fundamental systems: ES Modules (ESM) and CommonJS (CJS). Each has its unique characteristics and applications.
- ESM embodies modernity with its
import/export
syntax, facilitating asynchronous module loading and alignment with current web development practices. It's designed to be the go-to choice for new projects. - CJS, rooted in the familiar
require/module.exports
approach, offers a reliable and synchronous alternative. It continues to be valuable, especially within established Node.js environments. - Understanding the interplay between ESM and CJS is essential. However, for the future, it's wise to lean towards ESM. Why? Because it's now fully supported, and more importantly, it's future-proof. By choosing ESM for new projects, you ensure compatibility with emerging JavaScript standards and position your codebase for seamless integration with evolving technologies.