This sets the output format for the generated JavaScript files. There are three supported values now: esm
, cjs
, and umd
. In this guide, we will discuss the differences between these formats and how to choose the right one for your library.
Library authors need to carefully consider which module formats to support. Let's understand ESM (ECMAScript Modules) and CJS (CommonJS) and when to use them.
a modern module system introduced in ES2015 that allows JavaScript code to be organized into reusable, self-contained modules. ESM is now the standard for both browser and Node.js environments, replacing older module systems like CommonJS (CJS) and AMD.
ESM: ESM is a modern module system introduced in ES2015 that allows JavaScript code to be organized into reusable, self-contained modules. ESM is now the standard for both browser and Node.js environments, replacing older module systems like CommonJS (CJS) and AMD.
CommonJS: CommonJS is a module system used in JavaScript, particularly in server-side environments like Node.js. It was created to allow JavaScript to be used outside of the browser by providing a way to manage modules and dependencies.
The following references are from Node Modules at War: Why CommonJS and ES Modules Can't Get Along.
require()
ESM scripts; you can only import ESM scripts, like this: import {foo} from 'foo'
import
statements like the one above.import
CJS scripts, but only by using the “default import” syntax import _ from 'lodash'
, not the “named import” syntax import {shuffle} from 'lodash'
, which is a hassle if the CJS script uses named exports. (Except, sometimes, unpredictibly, Node can figure out what you meant!)require()
CJS scripts, even with named exports, but it's typically not worth the trouble, because it requires even more boilerplate, and, worst of all, bundlers like Webpack and Rollup don't/won't know how to work with ESM scripts that use require()
..js
to .mjs
. Alternately, you can set "type": "module"
in package.json
, and then you can opt-out of ESM by renaming scripts from .js
to .cjs
. (You can even tweak just an individual subdirectory by putting a one-line {"type": "module"}
package.json
file in there.)For different shapes of libraries, the choice of module format may vary. Here are two common scenarios:
shipping only ESM is the best choice for libraries that are intended to be used in modern environments, such as browser applications or Node.js applications that support ESM. However, if the upstream library is in format of CJS, they only can import pure ESM by using dynamic import like const pureEsmLib = await import('pure-esm-lib')
.
The community is migrating to ESM, but there are still many projects using CJS. If you want to support both ESM and CJS, you can publish a dual package. For most library authors, offering dual formats is a safer and smoother way to access the best of both worlds. You could read antfu' blog post Publish ESM and CJS packages for more details.
Pros:
Cons:
UMD stands for Universal Module Definition, a pattern for writing JavaScript modules that can work universally across different environments, such as both the browser and Node.js. Its primary goal is to ensure compatibility with the most popular module systems, including AMD (Asynchronous Module Definition), CommonJS (CJS), and browser globals.
If you are building a library that needs to be used in both the browser and Node.js environments, UMD is a good choice. UMD can be used as a standalone script tag in the browser or as a CommonJS module in Node.js.
A detailed answer from StackOverflow: What is the Universal Module Definition (UMD)?
However, for frontend libraries, you still offer a single file for convenience, that users can download (from a CDN) and directly embed in their web pages. This still commonly employs a UMD pattern, it's just no longer written/copied by the library author into their source code, but added automatically by the transpiler/bundler.
And similarly, for backend/universal libraries that are supposed to work in node.js, you still also distribute a commonjs module build via npm to support all the users who still use a legacy version of node.js (and don't want/need to employ a transpiler themselves). This is less common nowadays for new libraries, but existing ones try hard to stay backwards-compatible and not cause applications to break.
lib.format
to umd
in the Rslib configuration file.lib.umdName
to the name of the UMD library.output.externals
to specify the external dependencies that the UMD library depends on, lib.autoExtension
is enabled by default for UMD.The following Rslib config is an example to build a UMD library.
lib.format: 'umd'
: instruct Rslib to build in UMD format.lib.umdName: 'RslibUmdExample'
: set the export name of the UMD library.output.externals.react: 'React'
: specify the external dependency react
could be accessed by window.React
.runtime: 'classic'
: use the classic runtime of React to support applications that using React version under 18.MF stands for Module Federation. Module Federation is an architectural pattern for JavaScript application decomposition (similar to microservices on the server-side), allowing you to share code and resources between multiple JavaScript applications (or micro-frontends).
Check out Advanced - Module Federation chapter for more details.