What is the difference between CommonJS and ES Modules in Node.js, and how do you enable each one?
Show answer — try answering out loud first
CommonJS (CJS) is Node's historical module system: it uses require() and module.exports, and loads modules synchronously. ES Modules (ESM) is the official JavaScript standard: it uses import and export, and its loading is asynchronous. You enable ESM by using the .mjs extension or by putting "type": "module" in the package.json. For new projects, ESM is recommended.
A "module" is simply a file that exports things (functions, objects, values) so that other files can use them. Node has two systems for this:
CommonJS (CJS) — Node's original:
- To export:
module.exportsorexports. - To import:
require(). - Loading is synchronous: when you do
require, Node reads and executes that file right then, halting execution until it finishes. This works well because the files are on local disk. requireis dynamic: you can call it inside anifor in the middle of a function.
ES Modules (ESM) — the modern JavaScript standard (the same one used in the browser):
- To export:
exportandexport default. - To import:
import. - Loading is asynchronous: the engine first analyzes all the imports before executing. That's why
import(the static form) must go at the top of the file, not inside anif. (There is a dynamicimport(), which does return a promise and can be used anywhere.) - It's the future: it supports
top-level awaitand is compatible with browsers.
How to enable ESM in Node:
- Use the
.mjsextension in your files, or - Put
"type": "module"in yourpackage.json(this way all.jsfiles are treated as ESM). In that case, if you need an occasional CommonJS file, you use the.cjsextension.
When to use each one?
- ESM: new projects, code you share with the frontend, when you want
top-level await. It's the recommended direction today. - CommonJS: old projects or libraries, or when you depend on packages that don't offer an ESM version yet. It's still perfectly valid and very common.
// ===== CommonJS =====
// file: matematicas.cjs
function sumar(a, b) {
return a + b;
}
module.exports = { sumar }; // we export an object with the function
// file: app.cjs
const { sumar } = require("./matematicas.cjs"); // require is synchronous
console.log(sumar(2, 3)); // 5
// ===== ES Modules =====
// file: matematicas.mjs
export function sumar(a, b) {
return a + b;
}
export default function restar(a, b) {
return a - b;
}
// file: app.mjs
import restar, { sumar } from "./matematicas.mjs"; // import goes at the top of the file
console.log(sumar(2, 3)); // 5
console.log(restar(5, 2)); // 3
// Alternative: enable ESM for all .js files via package.json
{
"name": "mi-proyecto",
"type": "module"
}
Mixing syntax without noticing: using require() in a file that Node treats as ESM (because it has "type": "module"), which produces the error require is not defined in ES module scope. Or the other way around, using import in a CommonJS file. Another frequent mistake is forgetting the file extension in ESM imports: in Node, import { sumar } from "./matematicas" can fail; you must write "./matematicas.mjs".
"CommonJS is Node's historical system: it uses require and module.exports, and loads modules synchronously, so require can go inside an if or a function. ES Modules is the official JavaScript standard: it uses import and export, its loading is asynchronous, and static imports must go at the top of the file. I enable ESM with the .mjs extension or by putting type module in the package.json, and in that case I use .cjs for the occasional CommonJS files. For new projects I prefer ESM because it's the standard, it works well with the frontend, and it supports top-level await; I keep CommonJS when I'm working with old code or dependencies."
You have a package.json with "type": "module" and this index.js file. What happens when you run it?
const fs = require("fs");
console.log("hola");
See answer
It fails with an error similar to: ReferenceError: require is not defined in ES module scope.
Since the package.json has "type": "module", Node treats index.js as ESM, and in ESM require doesn't exist. The solution is to use import fs from "fs"; (or import fs from "node:fs";), or else rename the file to index.cjs if you really want to use CommonJS.