Topic 12 of 55
CommonJS & ESM
Overview
Node.js has two module systems: CommonJS (require/module.exports) — the original; and ES Modules (import/export) — the modern standard. Understanding both is essential as the ecosystem transitions from CJS to ESM.
Syntax
javascript
// CommonJS (CJS) — older, still widely used
const path = require('path');
const { readFile } = require('fs/promises');
module.exports = { myFunction }; // named export
module.exports = MyClass; // default export
// ES Modules (ESM) — modern standard
import path from 'path';
import { readFile } from 'fs/promises';
export function myFunction() {} // named export
export default class MyClass {} // default export
// Dynamic import (works in both CJS and ESM)
const module = await import('./heavy-module.js');
// package.json to use ESM
{
"type": "module" // enables .js files as ESM
}Common Pitfalls
- CJS uses synchronous require() which can cause issues with circular dependencies — ESM handles circular imports better.
- In ESM, you cannot use require, __dirname, or __filename — use import.meta.url and fileURLToPath instead.
- Interview tip: The ecosystem is migrating to ESM. New packages ship ESM-first. But many tools (Jest, some Webpack configs) still require CJS compatibility.
Real-World Example
A utility module with both CJS and ESM exports
example
javascript
// utils/formatters.js
export function formatCurrency(amount, currency = "INR") {
return new Intl.NumberFormat("en-IN", {
style: "currency",
currency,
maximumFractionDigits: 0,
}).format(amount);
}
export function formatDate(date, locale = "en-IN") {
return new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "long",
day: "numeric",
}).format(new Date(date));
}
export function slugify(text) {
return text
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}