Skip to content

/compare/nodejs/require-vs-import

require vs import in Node.js: CommonJS vs ES modules

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · v24.20.0updated
tool
Node.js
compares
require (CommonJS) vs import (ES modules)

require() is the CommonJS loader: a function that reads and runs a module synchronously and returns its module.exports. import is ECMAScript module (ESM) syntax: declarations are static, resolved before the code runs, can use top-level await and work the same in browsers. For new Node.js code use ESM ("type": "module"); require remains supported, and recent Node.js versions can even require() most ES modules.

Criterion by criterion

Criterionrequire (CommonJS)import (ESM)
Syntaxconst x = require("x")import x from "x"
When it resolvesAt runtime, wherever the call isBefore execution (static); import() for dynamic loading
Top-level awaitNoYes
Relative pathsExtension optional (./util)Extension required (./util.js)
Globals__dirname, __filename, module, exportsimport.meta.dirname, import.meta.filename, import.meta.url
ExportsA copied value (module.exports)Live bindings
Loading the other systemrequire(esm) works for ESM without top-level await (20.19+, 22.12+)import of CommonJS works; named exports detected statically
File is this system when.cjs, or .js without "type": "module".mjs, or .js with "type": "module"

Static vs runtime

bash
echo 'export const answer = 42;' > lib.mjs
cat > main.mjs <<'EOF'
import { answer } from "./lib.mjs";
const { readFile } = await import("node:fs/promises");   // dynamic import, top-level await
console.log(answer, typeof readFile);
EOF
node main.mjs
cat > main.cjs <<'EOF'
const path = require("node:path");
if (process.argv.length > 1) console.log(path.extname(__filename));
EOF
node main.cjs

Output

42 function
.cjs

Mixing the two

bash
echo 'export default function hi() { return "hi from ESM"; }' > esm.mjs
echo 'exports.add = (a, b) => a + b;' > cjs.cjs
cat > use-esm.cjs <<'EOF'
const m = require("./esm.mjs");                 // require(esm)
console.log(m.default());
EOF
node use-esm.cjs 2>&1
cat > use-cjs.mjs <<'EOF'
import { add } from "./cjs.cjs";               // import of CommonJS
console.log(add(2, 3));
EOF
node use-cjs.mjs

Output

hi from ESM
5

Named imports from CommonJS work when Node.js can detect the export names statically (exports.add = ...); for exports it cannot detect, such as module.exports = { add: (a, b) => a + b }, Node.js throws SyntaxError: Named export 'add' not found and suggests import pkg from "./cjs.cjs" instead. On Node.js versions without require(esm) support, the first script fails with ERR_REQUIRE_ESM; use await import("./esm.mjs") there.

Sources