Node.js modules split an application into small, self-contained files where variables stay private unless explicitly exported. Breaking routing, database queries, and formatting helpers into separate modules prevents global namespace conflicts and simplifies unit testing.
require) vs ES Modules (import)Node.js supports two module systems: the classic CommonJS (CJS) format used in legacy codebases and the JavaScript-standard ES Modules (ESM) format used in modern Node.js projects.
| Feature | CommonJS (.cjs / default) |
ES Modules (.mjs or "type": "module") |
|---|---|---|
| Import Syntax | const fs = require('node:fs') |
import fs from 'node:fs' |
| Export Syntax | module.exports = { fn } |
export function fn() {} |
| Loading Mode | Synchronous at runtime | Asynchronous static graph |
Top-Level await |
Not supported | Supported natively |
| Path Globals | __dirname, __filename |
import.meta.dirname (Node 20.11+) |
In an ES module project ("type": "module" in package.json), always include the .js file extension on relative local imports (./math.js).
// tax.js (ES Module)
export function addSalesTax(amount, rate = 0.08) {
return Number((amount * (1 + rate)).toFixed(2));
}
// checkout.js
import { addSalesTax } from './tax.js';
console.log('Total due:', addSalesTax(49.5));
In a CommonJS file, assign public functions to module.exports and load them with require('./tax').
// tax.cjs (CommonJS)
function addSalesTax(amount, rate = 0.08) {
return Number((amount * (1 + rate)).toFixed(2));
}
module.exports = { addSalesTax };
.js in ESM relative imports: Writing import { helper } from './helper' throws ERR_MODULE_NOT_FOUND in native Node.js ESM. Always include .js.exports = myFunc in CommonJS: Reassigning exports breaks the reference to module.exports and exports an empty object {}. Always assign module.exports = myFunc.node: prefix on built-in modules: Always write import path from 'node:path' so Node never accidentally resolves a malicious third-party package named path.Use ES Modules ("type": "module" in package.json). ESM matches browser JavaScript and TypeScript standards, enables top-level await, and supports cleaner tree-shaking.
__dirname inside an ES module?In Node.js 20.11+, read import.meta.dirname directly. In older Node 18/20 releases, compute it via path.dirname(fileURLToPath(import.meta.url)).
Yes. ESM files can import almost any CommonJS npm package via default import (import pkg from 'cjs-pkg').
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.