Skip to content
UniCircle
MediumConceptualModules

CommonJS vs ES modules, and the module wrapper

Question NODE-MOD-001

How do modules work in Node.js? Compare CommonJS (require/module.exports) with ES modules (import/export). What is the module wrapper function?

Show the explanation

Model answer. Each file is its own module with private scope; only what you export is visible to others.

CommonJS (CJS), Node's original system:

JavaScript
// math.js
const add = (a, b) => a + b;
module.exports = { add };          // or: exports.add = add
// app.js
const { add } = require('./math');

require is synchronous and can be called anywhere, even conditionally.

ES modules (ESM), the JavaScript standard:

JavaScript
export const add = (a, b) => a + b;   // named export
export default function log() {}      // one default export
import log, { add } from './math.js';

ESM imports are static (analysed before running, which enables tree-shaking), loaded asynchronously, strict by default, and allow top-level await. Enable ESM with "type": "module" in package.json or the .mjs extension.

Module wrapper (CJS): before running a CommonJS file, Node wraps it in a function:

JavaScript
(function (exports, require, module, __filename, __dirname) { /* your code */ });

That is why top-level variables stay private to the file, and where require, module and __dirname come from. (In ESM, use import.meta.url / import.meta.dirname instead.) If you never assign to module.exports, importers get an empty object.

Original UniCircle question. Last reviewed 22 Oct 2026.