JavaScript Modules: The Complete Import/Export Guide
Master ES6 modules. Learn named exports, default exports, dynamic imports, and how to organize your code effectively.
Why Modules?
Before ES6, JavaScript had no built-in module system. Code organization was a mess of global variables and script tags. ES6 modules solved this by letting you:
- Split code into separate files - Explicitly declare dependencies - Avoid polluting the global namespace - Enable tree-shaking (removing unused code)
Named Exports
Export multiple things from a module by name.
Exporting
``javascript
// math.js
// Export individually export const PI = 3.14159;
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }
// Or export all at once const subtract = (a, b) => a - b; const divide = (a, b) => a / b;
export { subtract, divide };
`
Importing
`javascript
// Import specific items
import { PI, add, multiply } from './math.js';
console.log(PI); // 3.14159 console.log(add(2, 3)); // 5
// Import with alias import { multiply as mult } from './math.js'; console.log(mult(2, 3)); // 6
// Import everything as namespace
import * as math from './math.js';
console.log(math.PI); // 3.14159
console.log(math.add(2, 3)); // 5
`
Default Exports
Each module can have ONE default export - the "main" thing.
Exporting
`javascript
// User.js
// Default export (can be anonymous) export default class User { constructor(name) { this.name = name; }
greet() {
return Hello, I'm ${this.name};
}
}
// Or export separately
class User { /* ... */ }
export default User;
`
Importing
`javascript
// Default imports don't use braces
// Name can be anything you want
import User from './User.js';
import MyUser from './User.js'; // Same thing, different name
const alice = new User('Alice');
`
Mixing Named and Default
`javascript
// api.js
export const API_URL = 'https://api.example.com';
export const timeout = 5000;
export default function fetchData(endpoint) {
return fetch(API_URL + endpoint);
}
`
`javascript
// Importing both
import fetchData, { API_URL, timeout } from './api.js';
// Or with alias
import { default as fetch, API_URL } from './api.js';
`
Re-exporting
Combine exports from multiple modules into one.
`javascript
// utils/index.js
// Re-export everything export * from './math.js'; export * from './string.js';
// Re-export specific items export { format, parse } from './date.js';
// Re-export with rename export { helper as utilHelper } from './helper.js';
// Re-export default as named
export { default as User } from './User.js';
`
`javascript
// Now import from single location
import { add, multiply, format, User } from './utils/index.js';
`
Dynamic Imports
Load modules on demand with import().
`javascript
// Static import (top of file, always loaded)
import { heavy } from './heavy.js';
// Dynamic import (loaded when needed) async function loadHeavyModule() { const module = await import('./heavy.js'); module.heavy(); }
// Conditional loading if (needsChart) { const { Chart } = await import('./chart.js'); new Chart(data); }
// With error handling
try {
const module = await import('./optional.js');
module.doSomething();
} catch (error) {
console.log('Module not available');
}
`
Code Splitting Example
`javascript
// Route-based code splitting
const routes = {
'/': () => import('./pages/Home.js'),
'/about': () => import('./pages/About.js'),
'/contact': () => import('./pages/Contact.js'),
};
async function loadPage(path) {
const loader = routes[path];
if (loader) {
const module = await loader();
module.default.render();
}
}
`
Module Patterns and Best Practices
1. Barrel Exports (Index Files)
`javascript
// components/index.js
export { Button } from './Button.js';
export { Input } from './Input.js';
export { Modal } from './Modal.js';
// Usage
import { Button, Input, Modal } from './components';
`
2. Avoid Default Export for Utilities
`javascript
// BAD - hard to know what you're getting
import utils from './utils';
// GOOD - explicit about what you need
import { formatDate, parseDate } from './utils';
`
3. One Component Per File
`javascript
// Button.js
export function Button({ children, onClick }) {
return ;
}
`
4. Constants in Separate Files
`javascript
// constants.js
export const API_URL = 'https://api.example.com';
export const MAX_RETRIES = 3;
export const TIMEOUT = 5000;
`
Common Mistakes
Mistake 1: Forgetting File Extension
`javascript
// In browsers, extension is required
import { add } from './math'; // Error!
import { add } from './math.js'; // Correct
// In bundlers (Webpack, Vite), extension often optional
import { add } from './math'; // Works
`
Mistake 2: Circular Dependencies
`javascript
// a.js
import { b } from './b.js';
export const a = 'A' + b;
// b.js import { a } from './a.js'; export const b = 'B' + a; // Problem!
// Fix: Restructure or use dynamic imports
`
Mistake 3: Side Effects in Modules
`javascript
// BAD - runs on import
let count = 0;
export function increment() { return ++count; }
// GOOD - explicit initialization
export function createCounter() {
let count = 0;
return { increment: () => ++count };
}
`
Summary
- Named exports: Multiple exports, import with { }
- Default export: One per module, import without { }
- Re-exports: Combine modules with export ... from
- Dynamic imports: Load on demand with import()`
- Use named exports for utilities, default for main components
- Create index.js barrel files for cleaner imports