CommonJS vs ES Modules
JavaScriptThe short answer
CommonJS and ES Modules are two different ways to organize and share code between JavaScript files. CommonJS uses require() and module.exports, and it was created for Node.js. ES Modules use import and export, and it is the official standard built into the JavaScript language. ES Modules are the future, but CommonJS is still widely used in Node.js.
CommonJS
CommonJS was created for Node.js because JavaScript did not have a module system when Node.js was first built.
Exporting:
// math.jsfunction add(a, b) { return a + b;}function subtract(a, b) { return a - b;}module.exports = { add, subtract };Importing:
// app.jsconst { add, subtract } = require('./math');console.log(add(2, 3)); // 5Key things about CommonJS:
require()is synchronous — it loads the file immediately and blocks execution until done- You can use
require()anywhere in your code, even insideifstatements - It runs at runtime — the module is loaded when the code reaches
require()
ES Modules
ES Modules (ESM) were introduced in ES6 (2015) as the official module system for JavaScript.
Exporting:
// math.jsexport function add(a, b) { return a + b;}export function subtract(a, b) { return a - b;}// or use default exportexport default function multiply(a, b) { return a * b;}Importing:
// app.jsimport multiply, { add, subtract } from './math.js';console.log(add(2, 3)); // 5Key things about ES Modules:
importstatements must be at the top of the file — you cannot put them insideifstatements- They are asynchronous — the browser or runtime can load them in parallel
- They are analyzed at compile time (statically), before the code runs
- They support tree shaking — bundlers can remove unused exports
The key differences
1. Syntax:
// CommonJSconst fs = require('fs');module.exports = { myFunction };// ES Modulesimport fs from 'fs';export { myFunction };2. Loading behavior:
CommonJS loads modules synchronously. When you require() a file, execution stops until the file is fully loaded. ES Modules are loaded asynchronously and analyzed statically before execution.
3. When they run:
CommonJS — at runtime. The require() call is evaluated when the code reaches that line. You can compute the module path dynamically:
// This works in CommonJSconst module = require(`./${condition ? 'a' : 'b'}`);ES Modules — at compile time. Imports are analyzed before any code runs. You cannot compute the import path:
// This does NOT work with static importimport module from `./${condition ? 'a' : 'b'}`; // SyntaxErrorFor dynamic imports in ES Modules, you use the import() function:
const module = await import(`./modules/${name}.js`);4. Tree shaking:
This is one of the biggest advantages of ES Modules. Because imports are static, bundlers like Webpack and Vite can analyze which exports are actually used and remove the unused ones. This reduces your bundle size.
CommonJS cannot be tree-shaken because require() is dynamic — the bundler cannot know at build time which parts of a module are used.
5. Default exports:
// CommonJS — module.exports is the defaultmodule.exports = function () {};const myFunc = require('./myFunc');// ES Modules — explicit default exportexport default function () {}import myFunc from './myFunc';// ES Modules also have named exportsexport function helper() {}import { helper } from './myFunc';What should you use?
- Frontend code — always ES Modules. Every modern bundler (Webpack, Vite, Rollup) and every browser supports them.
- Node.js — ES Modules are now fully supported. New projects should use ESM. You can either use
.mjsfile extension or add"type": "module"to yourpackage.json. - Older Node.js projects — you will still see CommonJS everywhere. Many npm packages still use CommonJS.
Common Pitfalls
A common mistake is mixing CommonJS and ES Modules in the same project without understanding the rules. You cannot use require() inside an ES Module file. And you cannot use top-level import in a CommonJS file without changing the file extension or package configuration. This often causes confusing errors.
Interview Tip
Focus on three things: the syntax difference, the loading behavior (sync vs async), and tree shaking. The tree shaking point is especially important because it directly affects application performance. If you can explain why ES Modules enable tree shaking and CommonJS does not, that shows a strong understanding.
Why interviewers ask this
Module systems are part of your everyday workflow as a frontend engineer. Interviewers ask this to see if you understand how code is organized in JavaScript projects, if you know the difference between the two systems, and if you can explain practical implications like tree shaking. It also shows if you have worked with both Node.js and frontend build tools.