← Back
commonjs javascript

Javascript modules

In javascript, a module is a file that exposes some code to other modules. JavaScript has had modules for a long time. However, they were implemented with libraries, and was part of the Javascript specification little time ago.

That situation has led to a proliferation of module formats, most prominently: CommonJS, AMD, and ES6 modules. A variation of CommonJS is what node.js uses, and the ES6 modules format is now part of the JS official specification.

CommonJS modules #

This is, by far, the most popular format since it's supported natively by node.js. More exactly, node implements a variation of CommonJS, that is the one I'll explain here.

I'll repeat it again: it's not part of the JS specification, but it's part of the node.js implementation, and there are lot of tools to convert that format into something browsers understand.

Most of node npm packages (if not all) are published using this module format.

Basically, a module is a file with a "module.exports" declaration where you specify the public part of the module.

For example, a file called "one.js":

function print(string) {
console.log(string);
}

function sayHello(name) {
print(`Hello ${name}`);
}

module.exports = { sayHello };

We are saying that the module "one" exports a function called "sayHello". We can't use the "print" function outside module one (isolation works!). But we can import the public part of that module from another using the "require" keyword.

Given a file called "two.js" in the same directory:

const one = require("./one");

one.sayHello("MarsBased");

(notice that the path is relative to the current file and no .js extension is required)

AMD modules #

AMD was a module format designed to load asynchronous code. Nowadays there are better solutions, so they are no longer used. I won't explain them.

ES6 modules #

Conceptually ES6 modules and CommonJS modules are very similar, but have different syntax and some important differences (I will only scratch the surface of those differences)

The syntax: instead of a module.export declaration, we use the export keyword to designate the public code.

The one.js module can be written in ES6 syntax:

function print(string) {
console.log(string);
}

export function sayHello(name) {
print(`Hello ${name}`);
}

Similarly, instead of require we use the keywords import and from. But instead of importing the whole module, we specify what parts of the module you want to import.

The `two.js` module in ES syntax is:

import { sayHello } from "./one";

sayHello('MarsBased');

However, there's a way to import all exported declarations from a module:

import * as one from "./one";

one.sayHello("MarsBased");

Another difference is that in ES6 modules you can specify one (and only one) default export that is imported by default (this is a ES6 module only feature).

For example, we could write one.js like this:

function print(string) {
console.log(string);
}

export default function sayHello(name) {
print(`Hello ${name}`);
}

And we can import the default export like this:

import sayHello from "./one";

sayHello("MarsBased");

The syntax is subtle but important: we are importing the default export, not the entry module. This does not work:

import one from "./one";

one.sayHello("MarsBased");

So, what format I should use? #

It depents a lot of the context. If your are a...

... a frontend developer #

On the frontend side the answer is clear: ES6 modules. Both CommonJS and ES6 modules are not supported (yet) by browsers, so you will need a tool to pack all the modules into one file that browser will understand.

There are lot of tools for that purpose: browserify, babel, webpack, rollup and parcel are the most popular. Some of them (like rollup) just convert the ES6 modules into other module formats (or one big file, like browserify), and some other (like babel, webpack or parcel) are capable of perform lot of transformations in between.

... a backend developer #

If you work on the backend side, the easy path is to go with commonJS modules, since they are natively supported.

In order to use ES6 modules in node.js you have two options: Convert them first to a CommonJS module format using webpack or babel (so you will need to run a "build" process before run) Use https://github.com/standard-things/esm

... a npm module publisher #

Things go crazy when you want to publish an npm package. The norm was to publish using CommonJS modules, but now it's recommended to publish in both formats: ES6 and CommonJS.

So you write the code using es6 modules and, before publishing, you create a CommonJS version of the modules (babel, rollup or webpack again) and publish both.

... an electron developer #

Electron uses both node.js and browser technologies. I'd recommend to stick with ES6 modules and convert them.

Summary #

ES6 modules are now the JS standard. You should use it. But now, you will need a build step to convert those modules into something your system (browser, node.js) understands.

Resources #