/compare/nodejs/require-vs-import
require vs import in Node.js: CommonJS vs ES modules
- 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
| Criterion | require (CommonJS) | import (ESM) |
|---|---|---|
| Syntax | const x = require("x") | import x from "x" |
| When it resolves | At runtime, wherever the call is | Before execution (static); import() for dynamic loading |
| Top-level await | No | Yes |
| Relative paths | Extension optional (./util) | Extension required (./util.js) |
| Globals | __dirname, __filename, module, exports | import.meta.dirname, import.meta.filename, import.meta.url |
| Exports | A copied value (module.exports) | Live bindings |
| Loading the other system | require(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
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.cjsOutput
42 function .cjs
Mixing the two
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.mjsOutput
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.