Node.js official docs are excellent at explaining the two different types of module systems. CommonJS and ESM.

There is a very good chapter in Node.js Design Patterns, Chapter 2: The Module System (2020) that describes where CommonJS came from. It emerges from the need to provide a module system for JavaScript in browserless environments simply because one did not exist, other than the AMD and UMD initiatives, and to not rely on urls and <script> tags for resources. It was successful so became popular.

We now have ECMAScript Modules thanks to the ES2015 official proposal and it attempts to align the management of modules in both server-side and browser environments.

The main points to help you decide are:

  1. CommonJS uses the require function to load modules and is synchronous. As a result, when you assign a module to module.exports for example, it too must be synchronous. If you have asynchronous stages in your module then you can export an uninitialized module but using require will mean that there is a chance it won't be ready to use in the code that requires it before it's initialized. An example of exporting and importing might look like this:
// titleCase.cjs

function titleCase(str) {
   if (!str){
      return '';
   }
   return str.toLowerCase().split(' ').map(word => {
      if (!word){
         return word;  
      }else{
         return word.charAt(0).toUpperCase() + word.slice(1);
      }
   }).join(' ');
}

module.exports = titleCase;
// app.js

const titleCase = require('./titleCase');
console.log(titleCase('the good, the bad and the ugly'));

// "The Good, The Bad And The Ugly"
  1. ES modules use the import keyword to load modules and the export keyword to export them. They are static, so they need to be described at the top level of every module and support loading modules asynchronously. ES modules facilitate static analysis of the dependency tree, making dead code elimination (tree shaking) more efficient. The same titleCase function above using ESM would look like:
// titleCase.js

export default function titleCase(str) {
   if (!str){
      return '';
   }
   return str.toLowerCase().split(' ').map(word => {
      if (!word){
         return word;  
      }else{
         return word.charAt(0).toUpperCase() + word.slice(1);
      }
   }).join(' ');
}
// app.js

import titleCase from './titleCase.js'; //< File extension required on import
console.log(titleCase('the good, the bad and the ugly'));

// "The Good, The Bad And The Ugly"
  1. ES modules run implicitly in strict mode (can't be disabled). This is a good thing in many opinions as it enforces good coding practices.
  2. In CommonJS you can use the helpful __filename and __dirname. In ES modules you need to do a workaround to get the same functionality like so:
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
  1. You can dynamically load modules in CommonJS with require:
let usefulModule;
if (process.env.DEV === true) {
   usefulModule = require('./utils/devmodule')
} else {
   usefulModule = require('./utils/prodmodule')
}
  1. And in ESM by using import as function:
let lib;
if (process.env.DEV === true) {
   lib  = await import('./utils/devmodule.js');
} else {
   lib  = await import('./utils/prodmodule.js');
}
const usefulModule = lib.usefulModule;

One thing I have noticed over the years of programming is that languages and libraries evolve. I have observed a growing shift in Node package docs giving more and more of their examples in ESM format. CommonJS is still the dominant option on npm but it tells me the ecosystem is slowly shifting away from CommonJS and towards ESM. Maybe they will run in tandem but such is the nature of evolution, one becomes dominant over the others. All of the projects I work on use the ESM approach. Good luck deciding.

Answer from jQueeny on Stack Overflow
🌐
Reddit
reddit.com › r/node › common js vs es modules and why?
r/node on Reddit: Common JS vs ES Modules and why?
December 9, 2022 -

Hey, I'm working on a small side project and learning node and just can't wrap my head around what's happening here. Please forgive if this has already been answered for someone else. I searched around and the answers don't quite do it for me.

My situation is this. I've installed create-react-app, graphql, and apollo client/server. I'm working on getting the server set up and through that have added a few lines to the server file that use an import statement.

The guide that I was following to set up the server however uses Common JS. This confuses me.

I don't understand why one is used over the either. I'm getting an error now when I init the server file that says "Cannot use import statement outside of a module". I understand that this means that the import statement isn't valid in my current file but I don't know why it's invalid in this file but perfectly fine in my react typescript files.

Can someone please explain to me why/when I should be using import vs. require, and why it only works in certain files and folders?

Thank you in advance!

Top answer
1 of 1
33
CommonJS is old, but Node tries to hold on to backwards compatibility as much as it can, but forward compatibility is a whole 'nother story. The major difference is, import is an asynchronous means of including modules, where as require is synchronous. CommonJS does not support async in the root of a module, import itself does exist in CommonJS, just not in the root level of a module (file). If you create an async function, you can all and await import from inside of it. ESM will handle the root level async imports for you, and provide a whole realm of future features going forward. ESM can consume and use CommonJS modules, but CommonJS cannot consume ESM modules, it's a one way street. A module either has to explicitly output both an ESM and a CommonJS output, and be configured in such a way that it exposes both in a resolvable fashion. In some cases people use a simple wrapper to achieve this, but it does not come without it's pains. This is mostly only an issue if you're building something with the intention of publishing it to npm where it will be consumed by a wider audience. Because whether or not you support both impacts who can consume your API. The reason you get this error: Cannot use import statement outside of a module Is because the backend project where you're using import is currently set to be CommonJS. You should add: { ... type: "module" ... } to your package.json in your backend project. If you'd like to see a TypeScript project configured to support both commonjs and ESM, without relying on any additional dependencies to do so, you can see my csrf-csrf project here, which has a package.json and a tsconfig, and some scripts, configured to output and publish a module to npm which natively supports both.
🌐
Better Stack
betterstack.com › community › guides › scaling-nodejs › commonjs-vs-esm
CommonJS vs. ES Modules | Better Stack Community
February 2, 2025 - Despite its popularity in Node.js, CommonJS has no static analysis, meaning tools cannot optimize code by removing unused functions (no tree shaking). It also relies on synchronous execution, which is inefficient for browser-based applications. These drawbacks led to the development of ECMAScript modules, which offers better performance and different syntax...
Discussions

node.js - Is there any performance difference in ES modules vs Common JS modules in NodeJS servers? - Stack Overflow
Is there any performance advantage or disadvantage of using ES modules over CommonJS modules in a nodejs backend project? I'm asking this because I see a lot of people using CJS still even though n... More on stackoverflow.com
🌐 stackoverflow.com
Is it better to use es6 modules with Node?
It’s basically a transition moment right now. The import/export syntax didn’t exist on the front-end when Node made the require syntax. Once import/export was added as a language feature, Node began to support it but not all existing modules have been updated. In fact, most probably haven’t as import/export is a relatively new feature. So, my advice would be to stick with require for the time being but plan to switch eventually. (I’m personally probably going to wait a year or so. When I do decide to switch, I’m going to test each module one-by-one and see which ones have or haven’t been updated. If it’s been that long without an update, it might be time to look for an alternative anyway.) More on reddit.com
🌐 r/node
28
39
April 9, 2021
CommonJS vs AMD vs RequireJS vs ES6 Modules
https://medium.com/computed-comparisons/commonjs-vs-amd-vs-requirejs-vs-es6-modules-2e814b114a0b Share More on reddit.com
🌐 r/javascript
1
1
September 30, 2018
ELI5: CommonJS vs AMD vs ES6

AMD is dead (was killed by Node-compatible bundlers). CommonJS is dead (was killed by Node). Node uses something that looks like CommonJS and is commonly called CommonJS but isn't actually CommonJS (there's a spec for CommonJS imports and Node knowingly violates good chunks of it).

ES6 imports aren't currently natively supported in any JS engine I know of. They can however be translated to Node-style "CommonJS" and there are bundlers that support them directly.

As for what CommonJS is: CommonJS was an effort to define common standards for the various competing server-side JavaScript environments that existed at the time (including well-known but now mostly obsolete stuff like Rhino as well as a few lesser known alternatives to Node that have died out). Node mostly won, so the new common standard is "whatever Node does".

One of the reasons AMD failed was that it is a lot more complex than the alternative. AMD is intended to be asynchronous at runtime. This means it not only defines dependencies, it also conflates the issue of lazy loading dependency bundles. As it turned out, lazy loading dependency bundles at runtime is a difficult, rare and heterogeneous enough problem that a module system can't easily solve it. But having this kind of complexity in it meant that it was effectively overengineered for the more common simple problems.

That said, if you write code today, just use either Node-style imports directly or a tool like Babel that translates ES6 imports to Node-style ones.

More on reddit.com
🌐 r/javascript
5
7
December 7, 2015
People also ask

Is it worth migrating a CommonJS project to ES Modules?
It depends on your tooling and dependencies. Migration improves long‑term maintainability but requires non‑trivial changes.
🌐
syncfusion.com
syncfusion.com › blogs › javascript › commonjs vs es modules in javascript: core differences, practical use cases, and best practices
CommonJS vs ES Modules in JavaScript: Core Differences, Practical ...
Top answer
1 of 3
48

Node.js official docs are excellent at explaining the two different types of module systems. CommonJS and ESM.

There is a very good chapter in Node.js Design Patterns, Chapter 2: The Module System (2020) that describes where CommonJS came from. It emerges from the need to provide a module system for JavaScript in browserless environments simply because one did not exist, other than the AMD and UMD initiatives, and to not rely on urls and <script> tags for resources. It was successful so became popular.

We now have ECMAScript Modules thanks to the ES2015 official proposal and it attempts to align the management of modules in both server-side and browser environments.

The main points to help you decide are:

  1. CommonJS uses the require function to load modules and is synchronous. As a result, when you assign a module to module.exports for example, it too must be synchronous. If you have asynchronous stages in your module then you can export an uninitialized module but using require will mean that there is a chance it won't be ready to use in the code that requires it before it's initialized. An example of exporting and importing might look like this:
// titleCase.cjs

function titleCase(str) {
   if (!str){
      return '';
   }
   return str.toLowerCase().split(' ').map(word => {
      if (!word){
         return word;  
      }else{
         return word.charAt(0).toUpperCase() + word.slice(1);
      }
   }).join(' ');
}

module.exports = titleCase;
// app.js

const titleCase = require('./titleCase');
console.log(titleCase('the good, the bad and the ugly'));

// "The Good, The Bad And The Ugly"
  1. ES modules use the import keyword to load modules and the export keyword to export them. They are static, so they need to be described at the top level of every module and support loading modules asynchronously. ES modules facilitate static analysis of the dependency tree, making dead code elimination (tree shaking) more efficient. The same titleCase function above using ESM would look like:
// titleCase.js

export default function titleCase(str) {
   if (!str){
      return '';
   }
   return str.toLowerCase().split(' ').map(word => {
      if (!word){
         return word;  
      }else{
         return word.charAt(0).toUpperCase() + word.slice(1);
      }
   }).join(' ');
}
// app.js

import titleCase from './titleCase.js'; //< File extension required on import
console.log(titleCase('the good, the bad and the ugly'));

// "The Good, The Bad And The Ugly"
  1. ES modules run implicitly in strict mode (can't be disabled). This is a good thing in many opinions as it enforces good coding practices.
  2. In CommonJS you can use the helpful __filename and __dirname. In ES modules you need to do a workaround to get the same functionality like so:
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
  1. You can dynamically load modules in CommonJS with require:
let usefulModule;
if (process.env.DEV === true) {
   usefulModule = require('./utils/devmodule')
} else {
   usefulModule = require('./utils/prodmodule')
}
  1. And in ESM by using import as function:
let lib;
if (process.env.DEV === true) {
   lib  = await import('./utils/devmodule.js');
} else {
   lib  = await import('./utils/prodmodule.js');
}
const usefulModule = lib.usefulModule;

One thing I have noticed over the years of programming is that languages and libraries evolve. I have observed a growing shift in Node package docs giving more and more of their examples in ESM format. CommonJS is still the dominant option on npm but it tells me the ecosystem is slowly shifting away from CommonJS and towards ESM. Maybe they will run in tandem but such is the nature of evolution, one becomes dominant over the others. All of the projects I work on use the ESM approach. Good luck deciding.

2 of 3
1

ES modules are designed to be loaded statically, while CommonJS modules are mainly loaded dynamically and synchronously. This can lead to slower performance and a blocking of the main thread. Static analysis refers to the process of analyzing code without executing it, and dynamic imports introduce runtime behavior into a module system. This means that the exact module that will be imported cannot be determined until the code is executed, making it difficult to analyze the module dependencies and relationships ahead of time (AOT).

Read this article please. https://dev.to/costamatheus97/es-modules-and-commonjs-an-overview-1i4b

🌐
LogRocket
blog.logrocket.com › home › commonjs vs. es modules in node.js
CommonJS vs. ES modules in Node.js - LogRocket Blog
June 20, 2024 - CommonJS modules load using require, and variables and functions export from a CommonJS module with module.exports. The ES module format was introduced in Node.js v8.5.0 as the JavaScript module system was standardized.
🌐
DEV Community
dev.to › stalwartcoder › es-modules-or-commonjs-whats-the-deal-and-why-should-i-care--3hem
ES Modules or CommonJS: What's the Deal and Why Should I Care ? - DEV Community
May 10, 2023 - Back in the day, JavaScript didn't have a built-in module system. So, dev's couldn't easily separate their code into different files or manage dependencies. It was kind of a mess. But then, Node.js stepped up with its own module system, CommonJS. And later, ES6 (ES2015) introduced ES Modules.
Find elsewhere
🌐
DEV Community
dev.to › costamatheus97 › es-modules-and-commonjs-an-overview-1i4b
ES Modules and CommonJS: An Overview - DEV Community
February 3, 2023 - Better Performance: ES modules are designed to be loaded statically, while CommonJS modules are mainly loaded dynamically and synchronously. This can lead to slower performance and a blocking of the main thread.
🌐
Medium
medium.com › @bloodturtle › understanding-the-difference-between-commonjs-and-es-modules-5c7262370770
Understanding the Difference Between CommonJS and ES Modules | by Bale | Medium
July 3, 2025 - However, CommonJS modules can still be used in the front-end via bundlers like Webpack or Browserify. ES Modules (ESM) were introduced as part of the ES6 (ECMAScript 2015) standard and are now natively supported in both modern browsers and Node.js (since v12+ with full support in v14+).
🌐
DEV Community
dev.to › saisathish › nodejs-modules-commonjs-vs-ecmascript-28da
Node.js Modules: CommonJS vs. ECMAScript - DEV Community
March 21, 2023 - ECMAScript modules, also known as ES modules, are a newer module system that was introduced in ECMAScript 6 (ES6). Unlike CommonJS, ES modules are asynchronous and use an import statement to load modules.
🌐
freeCodeCamp
freecodecamp.org › news › modules-in-javascript
Modules in JavaScript – CommonJS and ESmodules Explained
September 11, 2024 - As a side comment, Node used to only support CommonJS to implement modules, but nowadays it also supports ESmodules which is a more modern approach.
🌐
Aaron Bos
aaronbos.dev › posts › commonjs-vs-es-module
Exploring CommonJS and ES Modules
March 15, 2023 - It wasn't until mid-2018 that all major browsers shipped with the ability to natively import and export from modules in JavaScript, which was accomplished through ES Modules. The implementation details behind CommonJS and ES Modules are presumably quite different, but they both have the same goal of providing modules in JavaScript.
🌐
Medium
medium.com › globant › exploring-node-js-modules-commonjs-vs-es6-modules-2766e838bea9
Exploring Node.js Modules: CommonJS vs. ES6 Modules | by Jitesh Verma | Globant | Medium
May 8, 2023 - CommonJS modules have been the traditional method for handling dependencies in Node.js, whereas ECMAScript modules are a newer addition and follow the standard format used in modern browsers.
🌐
AppSignal
blog.appsignal.com › 2024 › 12 › 11 › a-deep-dive-into-commonjs-and-es-modules-in-nodejs.html
A Deep Dive Into CommonJS and ES Modules in Node.js | AppSignal Blog
December 11, 2024 - Modern syntax: The syntax of ESM is more aligned with modern JavaScript practices. It is considered cleaner and more intuitive than the CommonJS syntax. ES Modules are built around two key concepts: defining modules with export and consuming ...
🌐
W3Schools
w3schools.com › nodejs › nodejs_modules_esm.asp
Node.js ES Modules
Use Case: Dynamic imports are great ... on runtime conditions. Unlike CommonJS, ES Modules support top-level await, allowing you to use await outside of async functions at the module level....
🌐
JavaScript in Plain English
javascript.plainenglish.io › commonjs-vs-es-modules-why-both-matter-in-2025-b7edc1b29899
CommonJS vs ES Modules: Why Both Matter in 2025 | JavaScript in Plain English
September 7, 2025 - Okay, let’s keep this simple. Think of modules like LEGO blocks. You build small pieces, then snap them together to make something bigger. CommonJS is the old reliable way. It’s been around since 2009. You know when you type const something = require('something')?
🌐
Hoeser
hoeser.dev › blog › 2023-02-21-cjs-vs-esm
Snapstromegon - CommonJS vs. ESM
You can mark your file as a CommonJS module by either naming it with the .cjs extension, or by using the type: "commonjs" in your package.json. EcmaScript Modules (or ESM) got standardized later and are the only natively supported module style in browsers and all modern standard-compliant runtimes ...
🌐
NamasteDev
namastedev.com › home › technology & development › javascript › es modules vs commonjs: the modern way to import
ES Modules vs CommonJS: The Modern Way to Import - NamasteDev Blogs
October 15, 2025 - CommonJS loads modules synchronously, which means it retrieves a module before executing the code. In contrast, ES Modules can be loaded asynchronously, which can lead to better performance, especially in web applications where network requests ...
🌐
GreatFrontEnd
greatfrontend.com › questions › quiz › explain-the-differences-between-commonjs-modules-and-es-modules
Explain the differences between CommonJS modules and ES modules in JavaScript | Quiz Interview Questions with Solutions
September 5, 2021 - ... While CommonJS was the default module system in Node.js initially, ES modules are now the recommended approach for new projects, as they provide better tooling, performance, and ecosystem compatibility.
🌐
Michaelouroumis
michaelouroumis.com › en › blog › posts › module-pattern-es-modules-vs-commonjs
Module Pattern in JavaScript: ES Modules vs CommonJS – Michael Ouroumis Blog
May 27, 2025 - TL;DR: JavaScript’s two dominant module systems—ES Modules (import/export) and CommonJS (require/module.exports)—differ in syntax, loading strategy (static vs dynamic), tooling support, and ecosystem compatibility.