CommonJS vs ES Modules

Navigate the divide in the Node.js ecosystem. Learn when to use require() and when to embrace the modern import syntax.

Simulation ProgressStep 1 of 7
📦
0 EXP

Welcome to the Node.js runtime. Historically, Node.js used a system called CommonJS (CJS) to manage dependencies.

// Loading Node.js Runtime Environment...

CommonJS: The Node.js Legacy

CommonJS (CJS) is the default module system for Node.js. It was designed for the server, where loading files from the disk is fast and low-cost.

  • Import: Uses require('path'). This is a synchronous function call.
  • Export: Uses module.exports or exports.name.
const http = require('http');
module.exports = { start: () => {} };

Module Verification

Which object is used to export functionality in CommonJS?

Advanced Training Modules

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

📦
Module Master

Successfully convert a CommonJS module to ES Syntax.

Async Architect

Understand the asynchronous nature of ES Modules.

⚙️
Config Commander

Correctly configure package.json for ESM.

Mission: Refactor to ESM

The legacy system uses CommonJS. Update the code to use modern ES Module syntax (`import` and `export`).

Analysis Output:

> Awaiting changes...

Challenge: Migration Workflow

Order the steps logically to migrate a project to ES Modules.

Change file to "index.mjs"
Add "type": "module" to package.json
Use "import" and "export" syntax

Challenge: Configure & Import

Complete the package.json config and the module import statement.

// package.json
"type": "",
// app.js
express 'express';

Consult The Architect

Node.js Developer Hub

CommonJS vs ES Modules: The Node.js Divide

Node.js was born in 2009, years before JavaScript had an official module system. To solve the problem of organizing code, Node.js adopted **CommonJS** (CJS). For over a decade, `require()` and `module.exports` were the standard. However, the introduction of **ES Modules** (ESM) in ECMAScript 2015 (ES6) created a schism in the ecosystem that developers must now navigate.

1. Loading: Synchronous vs. Asynchronous

The most fundamental difference lies in how modules are loaded.

  • CommonJS is synchronous. When Node encounters `require('./file')`, it halts execution, reads the file from the disk, runs it, and returns the exports before moving to the next line. This is acceptable on servers where files are local.
  • ES Modules are asynchronous. The `import` statements are parsed statically before code execution. This allows for fetching modules over a network (crucial for browsers) and enables **Top-Level Await** in Node.js, allowing you to await promises at the root of your module.

2. Strict Mode and The "Global" Scope

ES Modules are always executed in **Strict Mode** (`"use strict"`). You cannot disable it. This implies certain safeguards, like variables not accidentally leaking to the global scope.

CommonJS Globals

You have access to magic variables:

console.log(__dirname);
console.log(__filename);

ESM Equivalents

`__dirname` does not exist. You must replicate it:

import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(import.meta.url);

3. Interoperability: Mixing Oil and Water

Node.js goes to great lengths to make them work together, but there are rules:

  • ESM can import CJS: You can use `import _ from 'lodash'` (a CJS package) inside an `.mjs` file. However, you can only import the *default export* (the `module.exports` object). Named imports (e.g., `import { map } from 'lodash'`) might not work unless the CJS package is specially configured.
  • CJS cannot `require` ESM: Because ESM is async, CommonJS cannot synchronously load it. If you need to load an ESM package in CJS, you must use the dynamic `import()` function: `const data = await import('./file.mjs')`.
Recommendation: For new Node.js projects, default to ES Modules by adding "type": "module" to your package.json. It is the standard for the entire JavaScript ecosystem (browsers, bundlers, Deno, Bun).

Node.js Modules Glossary

CommonJS (CJS)
The default module system in Node.js versions prior to 12. Uses `require()` and `module.exports`. Synchronous loading designed for server-side usage.
ES Modules (ESM)
The official JavaScript module standard (ECMAScript). Uses `import` and `export`. Asynchronous loading, static analysis, and browser-compatible.
Tree-Shaking
A form of dead code elimination used by bundlers (like Webpack or Rollup). It relies on the static structure of ES Modules to remove exports that are not imported.
`type`: "module"
A field in `package.json`. When set, Node.js treats all `.js` files in the project as ES Modules. Without it, `.js` is treated as CommonJS.
.mjs / .cjs
Explicit file extensions. `.mjs` forces a file to be treated as ESM, while `.cjs` forces CommonJS, regardless of the `package.json` settings.
Top-Level Await
A feature available in ES Modules that allows the use of the `await` keyword at the top level of a module, outside of async functions.

Credibility and Trust

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of Node.js experts, ensuring alignment with the latest Node.js LTS versions.

Verification and Updates

Last reviewed: October 2025.

Based on Node.js v20+ standards. We regularly update content to reflect the stabilizing ecosystem of ES Modules.

External Resources

Found an error or have a suggestion? Contact us!