A Comprehensive Overview of JavaScript Module Systems: From Traditional Scripts to ESM
This article traces the evolution of JavaScript module systems—from the early script tags and manual dependency ordering, through CommonJS, AMD, and UMD, to the modern ECMAScript modules—explaining their origins, challenges, and how bundlers and package fields enable seamless usage across browsers and Node.js environments.
If you have examined modern JavaScript libraries, you may be puzzled by the myriad module adaptation strategies; this article reviews the history and current state of JavaScript modularization, explaining why each system was created and what problems it solves.
Traditional JavaScript
Initially JavaScript ran only in browsers without any module system; scripts were loaded with simple <script src="..."></script> tags, requiring developers to manually order dependencies, which quickly became painful for larger projects.
Node.js Module Solution
Node.js introduced the CommonJS specification, making modules mandatory. A typical CommonJS module looks like:
define(function (require, exports, module) {
// use event module
var ec = require("event");
});Many libraries still provide only a CommonJS entry point.
Browser Module Solutions
Because CommonJS is synchronous and unsuitable for browsers, the AMD (Asynchronous Module Definition) standard was created, typically used with the RequireJS loader. An AMD module is defined as:
define(["beta"], function (beta) {
// call module
});AMD saw wide adoption but has largely fallen out of use.
Bridging the Gap: UMD
To allow a library to work in any environment, the Universal Module Definition (UMD) pattern was devised. A typical UMD wrapper detects AMD, CommonJS, or falls back to a global variable:
(function (root, factory) {
var Data = factory(root);
if (typeof define === 'function' && define.amd) {
define('data', function () { return Data; });
} else if (typeof exports === 'object') {
module.exports = Data;
} else {
var _Data = root.Data;
Data.noConflict = function () { if (root.Data === Data) { root.Data = _Data; } return Data; };
root.Data = Data;
}
}(this, function (root) {
var Data = ...;
// library code
return Data;
}));Even after UMD, the newer ECMAScript Module (ESM) standard introduced in ES2015 reshaped the landscape.
ESM and Build Tools
ESM provides native import and export syntax, but browsers and Node.js required tooling for full support. Early bundlers like Rollup added dual entry points (ESM and CommonJS). Webpack later added mainFields configuration to prioritize module over main . Example configuration screenshots are shown in the original article.
Handling Environment Differences
Libraries that need to run both in browsers and Node.js often use the browser field in package.json to replace Node‑specific modules with browser equivalents. Tools such as Webpack respect this field.
Node.js Support for ESM
Node.js supports ESM via file extensions (.mjs) or the type field in package.json . It can import CommonJS modules, but CommonJS cannot import ESM directly. The newer exports field further refines entry point selection for different environments.
Dual‑Package Pitfalls
Providing both ESM and CommonJS entries can lead to the “dual‑package” problem where the same library is executed twice if one dependency loads the ESM version while another loads the CommonJS version. Solutions include making the CommonJS version the core implementation and having the ESM version act as a thin wrapper, or dropping CommonJS support entirely.
Deno and URL‑Based Loading
Deno loads modules via URLs, and can consume npm packages through services like unpkg or esm, which require additional fields in package.json to map URLs to the correct entry points.
Conclusion
The article summarizes the major JavaScript module systems, their historical motivations, and how modern tooling bridges the gaps, enabling developers to use any module format seamlessly across browsers and server environments.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.