ES Modules in Node (import/export, package.json type)
What Are ES Modules?
ES Modules (ESM) are the official JavaScript module standard. They use import and export syntax and are supported by modern browsers and Node.js.
Basic Syntax
// math.js
export function add(a, b) {
return a + b;
}
// index.js
import { add } from './math.js';
console.log(add(2, 3));
Enabling ESM in Node
Node requires explicit configuration to treat files as ES Modules.
Option 1: package.json
{
"type": "module"
}
This tells Node that .js files are ES Modules.
Option 2: .mjs Extension
Files with .mjs are treated as ES Modules regardless of package.json.
File Extensions Are Required
Unlike CommonJS, ESM requires explicit file extensions:
import { add } from './math.js'; // correct
import { add } from './math'; // error in Node ESM
__dirname Is Not Available
ESM does not provide __dirname or __filename automatically.
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Top-Level await
ES Modules allow top-level await:
const data = await fetchData(); console.log(data);
Interoperability with CommonJS
Importing CommonJS modules in ESM is supported, but named exports may not behave as expected.
Production Pitfalls
- Forgetting file extensions.
- Mixing CJS and ESM incorrectly.
- Tooling mismatches (TypeScript config vs Node runtime).
TypeScript Configuration
When using TypeScript, set:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}
When to Choose ESM
- New projects
- Modern tooling
- Browser + Node shared libraries
When to Be Careful
- Legacy ecosystems
- Older deployment environments
Production Insight
Choose one module system per project. Mixing strategies without understanding resolution rules creates subtle runtime bugs.
Next Step
Next we will cover CommonJS (CJS) to understand how legacy and modern systems interact.