Understanding the Differences Between ESM and CommonJS in Node.js
A comprehensive guide on ESM vs CommonJS in Node.js, with practical examples, migration tips, and best practices.
nodejsesmcommonjs
Understanding the Differences Between ESM and CommonJS in Node.js
Modern JavaScript development has evolved significantly, and one of the biggest shifts has been the move from CommonJS (CJS) to ECMAScript Modules (ESM). If you've ever been confused about when to use require() versus import, this article will clear things up — complete with examples and best practices.
1. What Are Modules?
Modules are reusable pieces of code that can be imported or exported between files. They allow developers to keep code organized and avoid global namespace pollution.
In Node.js, there are two primary module systems:
CommonJS (CJS) – the original Node.js module system
ECMAScript Modules (ESM) – the standardized JavaScript module system adopted from browsers
2. CommonJS Overview
CommonJS has been the default in Node.js since its beginning. It uses the require() function for imports and module.exports for exports.
Example: Using CommonJS
// math.cjsfunction add(a, b) { return a + b;}function subtract(a, b) { return a - b;}module.exports = { add, subtract };
// app.cjsconst math = require('./math.cjs');console.log(math.add(2, 3)); // 5
CommonJS Characteristics
Synchronous loading (modules are loaded at runtime)
require() can be called anywhere in code
Uses module.exports and exports for exports
Default file extension is .cjs (if mixing with ESM)
Historically the default in Node.js
3. ECMAScript Modules (ESM) Overview
ESM is the official module system standardized by JavaScript (ECMAScript 2015+). It uses the import and export syntax.
Example: Using ESM
// math.jsexport function add(a, b) { return a + b;}export function subtract(a, b) { return a - b;}
// app.jsimport { add } from './math.js';console.log(add(2, 3)); // 5
ESM Characteristics
Asynchronous and supports tree-shaking
Must use import/export syntax
Top-level await is supported
File extension is .mjs or .js (with "type": "module" in package.json)
Static analysis possible — better for bundlers and performance
4. How to Enable ESM in Node.js
You can enable ESM in Node.js in two ways:
Option 1: Use .mjs Extension
node app.mjs
Option 2: Add to package.json
{ "type": "module"}
Then you can use .js files with import and export.
5. Key Differences Between CommonJS and ESM
Feature
CommonJS
ESM
Syntax
require() / module.exports
import / export
Loading
Synchronous
Asynchronous
Default Extension
.cjs
.mjs or .js
Top-level await
❌ Not supported
✅ Supported
Tree-shaking
❌ No
✅ Yes
Static Analysis
❌ Harder
✅ Easier
Use Case
Legacy or existing Node.js projects
Modern apps and libraries
6. Mixed Module Environments
You can still mix CommonJS and ESM, but with limitations.
You plan to share code with frontend or browser-based code
Use CommonJS if:
You maintain older Node.js projects
You depend on libraries that only support CJS
Best Practice (2025)
For all new Node.js projects, prefer ESM. It’s the standard moving forward and ensures compatibility with modern tools, frameworks, and JavaScript features.
10. Final Thoughts
While CommonJS has served developers well for over a decade, the JavaScript ecosystem has largely moved toward ESM. Understanding the key differences and knowing how to migrate ensures your projects stay maintainable, modern, and compatible with the evolving JavaScript landscape.