ES Modules (import/export)
Also Known As
ES modules
ESM
import export JS
JavaScript modules
TL;DR
The standard JavaScript module system — static imports/exports enable tree-shaking, top-level await, and strict mode by default.
Explanation
ES Modules (ESM, ES2015) use import and export statements for static, lexically scoped module boundaries. Named exports: export function fn() {} — import { fn } from './mod.js'. Default exports: export default class Foo — import Foo from './mod.js'. Dynamic imports: const mod = await import('./mod.js') — lazy-loads a module, returns a Promise. Key properties: modules are singletons (imported once, cached), strict mode by default, top-level await supported, and static analysis enables tree-shaking (dead code elimination). Compared to CommonJS (require/module.exports): ESM is statically analysable and natively supported in browsers; CJS is dynamic and synchronous. Node.js supports both; use .mjs or "type": "module" in package.json for ESM.
Common Misconception
✗ CommonJS (require) and ES modules (import) are interchangeable in modern Node.js. They have different semantics — CommonJS is synchronous and dynamic; ES modules are asynchronous, static, and support tree-shaking. Mixing them requires careful interop configuration and has edge cases around default exports.
Why It Matters
ES modules (import/export) provide static dependency analysis, tree-shaking, and proper scope isolation — replacing global script tags and CommonJS require() for browser code.
Common Mistakes
- Mixing ES module import and CommonJS require() in the same file — causes runtime errors.
- Not using named exports for utility functions — default exports cannot be tree-shaken as effectively.
- Circular imports that cause undefined references at runtime — restructure to break the cycle.
- Dynamic import() not used for code-splitting — large bundles load everything even when unused.
Code Examples
✗ Vulnerable
// Global script pollution — all vars accessible everywhere:
<script src="utils.js"></script> <!-- Adds Utils to window -->
<script src="app.js"></script> <!-- Overwrites if same variable names -->
// ES modules — scoped, explicit:
import { formatDate, formatCurrency } from './utils.js';
export function renderOrder(order) { /* ... */ }
✓ Fixed
// Named exports — import by name
// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14159;
// main.js
import { add, PI } from './math.js';
import { add as sum } from './math.js'; // alias
// Default export — one per file
// logger.js
export default class Logger { log(msg) { console.log(msg); } }
// main.js
import Logger from './logger.js'; // any name works
// Dynamic import — code splitting / lazy loading
const button = document.querySelector('#load-chart');
button.addEventListener('click', async () => {
const { Chart } = await import('./chart.js'); // loaded on demand
new Chart(document.querySelector('#canvas'));
});
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
24
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 1
No pings yesterday
Amazonbot 7
Perplexity 5
Majestic 2
Google 2
ChatGPT 1
Unknown AI 1
Ahrefs 1
Also referenced
How they use it
crawler 17
crawler_json 1
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟡 Medium
⚙ Fix effort: Low
⚡ Quick Fix
Use ES modules (import/export) in all new JavaScript — add type='module' to script tags or configure your bundler; avoid CommonJS require() in browser code as it prevents tree-shaking
📦 Applies To
javascript ES2015
web
cli
🔗 Prerequisites
🔍 Detection Hints
require() in browser code; no type=module on script tag; global variables used instead of modules for code organisation
Auto-detectable:
✓ Yes
eslint
esbuild
vite
⚠ Related Problems
🤖 AI Agent
Confidence: Low
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: File