Language: EN

modulos-commonjs-y-esm-nodejs

Differences between CommonJS and ESM modules in Node.js

We have seen that Node.js supports files with the .js and .mjs extensions. If that sounds a bit confusing, buckle up because there are twists ahead I’ll explain it right now.

CommonJS and ECMAScript Modules (ESM) are two module systems used in JavaScript to organize and reuse code across different files.

When Node.js appeared (2009), JavaScript did not have a native module system. Node.js decided to create its own, CommonJS. A decision that we are still carrying today.

Later, JavaScript created its “official” file system, ECMAScript Modules (ESM) (2015). They were a bit late, poor things. But the official syntax had many improvements over the previous ones.

You might say, “Well… ‘everything is perfect, let’s ditch CommonJS and use ESM!’“. By that time, there was so much code that it took years to replace them.

In fact, Node.js took until its version 12 (2019) to be compatible with ESM. That’s 10 long years of CommonJS. Therefore, there are still many tools and libraries that have not been ported.

That’s why there are two extensions:

  • .js works with CommonJS modules 🤔
  • .mjs works with ECMAScript Modules (ESM) 👍

Whenever possible, use ECMAScript Modules (ESM). They are the standard solution and should replace CommonJS.

Using modules in JavaScript

Let’s see how to use CommonJS and ECMAScript Modules. As mentioned, unless a library is not compatible or there is another compelling reason, we will prefer ECMAScript.

But we will look at the syntax of both, because you will find half of the documentation on the internet, libraries, or projects you encounter, in each system.

CommonJS Legacy

CommonJS is a module system that was developed for use in server environments, like Node.js. It uses the keywords require and module.exports to import and export modules.

Here’s an example of its use.

// Import a module
const anotherModule = require('./anotherModule');

// Export a module
module.exports = {
  foo: 'bar'
};

Node.js has used CommonJS as its main module system for a long time.

ECMAScript Modules (ESM) Standard

ECMAScript modules are the standard module system in JavaScript defined in the ECMAScript standard (the underlying standard for JavaScript). It uses the keywords import and export to import and export modules, respectively.

Example of its use:

// Import a module
import anotherModule from './anotherModule';

// Export a module
export const foo = 'bar';

ECMAScript modules are designed to work both in modern web browsers and server environments (like Node.js), which means they are more portable.

Additionally, ECMAScript modules are loaded asynchronously, which can be more efficient in certain scenarios.

JS, CJS, and MJS Extensions

Before the adoption of ECMAScript modules in Node.js, JavaScript files were written with the .js extension and used CommonJS for importing and exporting code.

Starting from Node.js v12, support for ECMAScript modules was introduced, allowing the same import/export syntax used in modern web browsers.

Since the “good” extension was already taken, they had to call it .mjs. To differentiate them, some (not many) started calling .cjs the files that contained CommonJS modules, which they previously called .js.

On the other hand, a .js file can be treated as an ESM module if its package.json file has the type set to “module”. Like this:

{
  "name": "your_module",
  "type": "module",
  "version": "1.0.0",
  // ... more stuff
}

So, if they try to complicate it more, they won’t succeed even if they wanted to 😅

How to use CommonJS modules in ESM

In general, you should try to use ESM modules. They are the standard and should always be used… and the world is wonderful and full of unicorns and rainbows 🦄.

No, the truth is that having two systems is a hassle, and you will have to learn to use both and coexist with both. Using one or the other is too difficult, but when you have to mix them… you might pull your hair out sometimes.

In principle, in your projects, you should use ESM modules. The Node.js documentation itself has migrated to using ESM. But there are still many libraries that have not been ported yet, and some that will never be ported.

So let’s see how to import a CommonJS module in ESM. Fortunately, it’s very simple.

import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);

const your_functions = require('your_commonjs_module');

Download the code

All the code from this post is available for download on Github github-full