Learn How To Use ECMAScript Modules in Node.js
ECMAScript Modules (ESM) have emerged as the modern standard for building modular and maintainable JavaScript applications.
ESM is used in most modern web applications, so it's crucial to understand how to leverage their potential in Node.js.
In this article, we will guide you through using ECMAScript Modules in Node.js, covering the basics, the differences between ESM and CommonJS, and best practices for making the most of this powerful feature.
Prerequisites
- Node version installed > 16
Understanding ECMAScript Modules (ESM)
ECMAScript Modules are a standard introduced in ECMAScript 2015 (ES6) to provide a more efficient and flexible way to organize and share JavaScript code.
ESM offers a clear syntax for importing and exporting functions, classes, and values between modules.
The primary benefits of using ESM in Node.js include: Improved code organization Enhanced performance with static imports and exports Better dependency management Simplified module loading
Enabling ESM in Node.js
I'll include two different ways here to easily enable ESM in your Node.js project.
Use the
.mjs
file extension: If you rename your file extensions to use.mjs
instead of.js
, Node.js will interpret these files as an ECMAScript module.Set the "type" field in package.json:
{ "type": "module" }
This setting informs Node.js that your project is using the ESM syntax.
By default, Node.js treats .js
files as CommonJS modules. However, with the "type" field set to "module" in your package.json, Node.js will now interpret .js
files as ESM. Alternatively, you can use the .mjs
file extension to denote an ECMAScript module explicitly.
Importing and Exporting with ESM
Importing and exporting code in ESM is more streamlined than in CommonJS. Here's how to use the import and export statements in ESM:
Exporting:
// myModule.mjs export const myFunction = () => {...}; export class MyClass {...};
You can also use the named export syntax:
// myModule.mjs export const myFunction = () => {...}; export class MyClass {...};
Or another example with the same results:
// myModule.mjs const myFunction = () => {...}; class MyClass {...}; // export as a list export { myFunction, MyClass };
And then another common pattern you will see if "default" exports:
// myModuleWithDefault.mjs // You can only have a single "default" export per module const myFunction = () => {...}; export default myFunction;
Check out the MDN docs here for a comprehensive list of examples of this syntax.
Importing
Now let's learn how to import:
import { myFunction, MyClass } from './myModule.js'; // Call myFunction myFunction()
You can also import everything from a module using the wildcard (*) syntax:
// index.js import * as MyModule from './myModule.js'; MyModule.myFunction(); const myInstance = new MyModule.MyClass();
Bonus: Dynamic Imports
Dynamic importing was introduced in Node.js as an experimental feature in version 10 and then stable in Node 14 (2020).
This is yet another reason I usually opt for modules over CommonJS by default.
ESM allows you to import modules dynamically using the import()
function. This feature is handy when loading modules on demand or conditionally.
Dynamic imports return a Promise that resolves to the imported module:
(async () => { if (condition) { const myModule = await import('./myModule.js'); myModule.myFunction(); } })();
I've a video here going into this in a little more detail here:
Differences between ESM and CommonJS
Some differences between ESM and CommonJS include the following:
- ESM uses the import and export keywords, while CommonJS uses
require()
andmodule.exports
. - ESM has a static module structure, meaning that imports and exports are determined at compile-time, which enables better optimization and tree shaking. CommonJS has a dynamic structure, with modules resolved at runtime.
- ESM supports named and default exports, while CommonJS only supports a single export object.
I hope this helped! 🚀
Follow me on Twitter or connect on LinkedIn.