JavaScript Modules
Organize code into reusable, maintainable modules.
Named Exports
Export multiple values from a single file.
// math.js
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
export const PI = 3.14159;
Import with destructuring. Use as for aliases.
import { add, subtract, PI } from './math.js';
import { add as sum } from './math.js';
import * as Math from './math.js';
Default Export
One default export per file. Import with any name.
// logger.js
export default class Logger {
constructor(name) { this.name = name; }
log(msg) { console.log(`[${this.name}] ${msg}`); }
}
import Logger from './logger.js';
Mixing Exports
Combine default and named exports.
// utils.js
export function formatDate(d) { return d.toLocaleDateString(); }
export default class Formatter {
static currency(amt) { return `$${amt.toFixed(2)}`; }
}
import Formatter, { formatDate } from './utils.js';
Re-exporting
Create barrel files to centralize imports.
export { add, subtract } from './math.js';
export { default } from './logger.js';
export * from './math.js';
Module Pattern for Privacy
Unexported variables are private to the module.
// counter.js
let count = 0;
export function getCount() { return count; }
export function increment() { return count++; }
export function decrement() { return count--; }
import { getCount, increment } from './counter.js';
increment();
console.log(getCount()); // 1
Best Practices
- Use named exports — For multiple exports
- Use default export — For single main export
- Keep modules focused — One responsibility
- Export public API only — Hide implementation
Common Mistakes
- Circular dependencies — Avoid modules importing each other
- Over-exporting — Export only what’s needed
- Forgetting
.jsextension — Required in browser ESM - Inconsistent naming — Use camelCase for exports