skip to content
Notes && Anecdotes
An illustration of imports of physical good. Open qustion: can you tree-shake a cargo ship? An illustration of imports of physical good. Open qustion: can you tree-shake a cargo ship?

ESM vs CommonJS


This is a topic I’ve been postponing to understand forever. What is what and why should I care as a web-developer.

What’s the syntax difference?

// commonjs
const { readFile } = require("fs");

// esm
import { readFile } from "fs";

So I guess I use esm today?

Hmm, not so fast. We, like many others, write our code with the esm import syntax, but our code is still being transpiled to commonjs by typescript. This is a setting in our tsconfig.json

So I don’t need to care

Well, maybe you should. If you use commonjs, you may have noticed some issues when installing the latest version of node-fetch? That’s because node-fetch is now using esm and you’re using commonjs.

Why can’t I use commonjs while node-fetch uses esm?

Because commonjs does not support import of esm modules. esm however, does support import of commonjs modules.

So I don’t have to care anymore if I use esm?

Right, spot on!

But things work today. Is there any other benefits I gain by changing to esm?

And downsides?

If you’re developing packages for others to consume, you might annoy commonjs consumers by publishing an esm package. node-fetch published this esm/commonjs FAQ as a response to all the questions after they transitioned to esm.

I believe a main difference being esm modules are imported asyncronously on use, while commonjs are imported sync immediately. If that’s correct, you could argue commonjs to be more suitable serverside?

Ok, lets go. How do I start using esm?

Follow this guide

In NextJS, rename next.config.js to next.config.mjs.

My imports are not working anymore. What’s up with that?

You need to add the file extension to your imports. So instead of import { fish } from "rod" you need to write import { fish } from "rod.js".

There’s a codemod for that, cjs2esm:

npx cjs2esm

__dirname and __filename are not defined anymore. What’s up with that?

__dirname and __filename are not defined in esm. You can use import.meta.url instead, like in the example below;

import * as url from 'url';
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

Is life better now?

Probably. You should check your apps bundle size before and after to see.