Learn How To Use ECMAScript Modules in Node.js

Node.js on a pink post-it

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.

  1. 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.

  2. 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() and module.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.

NodejsWeb DevelopmentTutorialJavaScriptJS
Avatar for Niall Maher

Written by Niall Maher

Founder of Codú - The web developer community! I've worked in nearly every corner of technology businesses; Lead Developer, Software Architect, Product Manager, CTO and now happily a Founder.

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.