JavaScript has two competing module systems in active use today — CommonJS (Node’s original) and ES Modules (the language’s official standard) — and knowing which is which matters for debugging import errors.
// CommonJS (Node's traditional default, .js with "type": "commonjs")
const fs = require('fs');
module.exports = { readConfig };
function readConfig() { /* ... */ }
// ES Modules (the language standard, .mjs or "type": "module" in package.json)
import fs from 'fs';
export function readConfig() { /* ... */ }
export default readConfig;
Key Practical Differences
- ESM imports are statically analyzable (enables tree-shaking in bundlers); CommonJS
require()is dynamic and can’t be analyzed the same way - ESM runs in strict mode by default; CommonJS does not
- Top-level
awaitworks in ESM, not in CommonJS - Browsers natively support ESM (
<script type="module">); they’ve never supported CommonJS directly
Node.js now fully supports both, but mixing them in the same project (importing a CommonJS package from ESM, or vice versa) still occasionally causes friction — worth checking a package’s actual module type before assuming it “just works” either way.