JavaScript Modules (ESM/CJS) Cheat Sheet
Covers ES module import/export syntax, CommonJS require/module.exports, Node.js module configuration, and interoperability between the two systems.
1 PageIntermediateApr 12, 2026
ES Modules (ESM)
The standard, static module system.
javascript
// math.jsexport const PI = 3.14159;export function add(a, b) { return a + b; }export default function multiply(a, b) { return a * b; }// app.jsimport multiply, { PI, add } from "./math.js"; // Default + named importsimport * as math from "./math.js"; // Namespace importexport { add as sum }; // Re-export with renameexport * from "./math.js"; // Re-export everything// Note: ESM import paths require file extensions in Node.js (./math.js, not ./math)
CommonJS (CJS)
Node's original, synchronous module system.
javascript
// math.jsconst PI = 3.14159;function add(a, b) { return a + b; }module.exports = { PI, add };// or: module.exports.add = add;// or a single default-like export: module.exports = add;// app.jsconst { PI, add } = require("./math.js");const math = require("./math.js"); // Whole module object// require() is synchronous and can be called conditionally/anywhere in the fileif (process.env.DEBUG) { const debug = require("./debug.js");}
Configuring Module Type in Node.js
package.json's "type" field decides how .js files are parsed.
json
// package.json{ "name": "my-app", "type": "module", "main": "index.js"}// "type": "module" -> .js files are treated as ESM// "type": "commonjs" (default) -> .js files are treated as CJS// Use .mjs to force ESM and .cjs to force CJS regardless of "type"
Interop Between ESM and CJS
Mixing the two systems has a few hard rules.
javascript
// Importing a CJS module from ESM: works, module.exports becomes the default exportimport pkg from "./legacy-cjs-module.js";const { someFn } = pkg;// Dynamic import works in both ESM and CJS, always returns a Promiseasync function load() { const mod = await import("./math.js"); mod.add(1, 2);}// require()-ing an ESM file from CJS throws --// ESM cannot be require()'d directly, use dynamic import() instead
Key Differences
What actually changes between the two systems.
- Loading- CJS require() is synchronous; ESM import is asynchronous under the hood
- Hoisting- ESM imports are hoisted and resolved before any code runs; CJS require() executes inline
- this at top level- undefined in ESM modules; module.exports in CJS modules
- __dirname / __filename- Available in CJS; in ESM, derive them via import.meta.url instead
- Tree-shaking- ESM's static import/export structure lets bundlers eliminate unused exports; CJS's dynamic nature mostly prevents it
- Live bindings- ESM named exports are live references to the source; CJS exports are copied values at require time
Pro Tip
Don't mix require() and import in the same file expecting them to behave identically -- if package.json has "type": "module", every .js file is parsed as ESM and require is undefined by default; use createRequire from 'module' if you truly need CJS require inside an ESM file.
Was this cheat sheet helpful?
Explore Topics
#JavaScriptModulesESMCJS#JavaScriptModulesESMCJSCheatSheet#Programming#Intermediate#ESModulesESM#CommonJSCJS#Configuring#Module#CheatSheet#SkillVeris
Advertisement
Sri Hayavadhana Info-Tech
Professional Web Designing Services
- Responsive Websites
- E-commerce Solutions
- SEO Friendly Design
- Fast & Secure
- Support & Maintenance