Modules
ES Modules and import/export
JavaScript Modules
Modules let you split your code into separate files, making it more organized and reusable. ES Modules (ESM) are the standard way to work with modules in modern JavaScript.
Exporting from a Module
// math.js
// Named exports
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// Or export multiple at once
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;
export { multiply, divide };
// Default export (only one per file)
export default function calculate(operation, a, b) {
switch(operation) {
case 'add': return a + b;
case 'subtract': return a - b;
default: return 0;
}
}
Importing from a Module
// app.js
// Import named exports
import { add, subtract, PI } from './math.js';
console.log(add(5, 3)); // 8
console.log(PI); // 3.14159
// Import with alias
import { multiply as mult } from './math.js';
console.log(mult(4, 5)); // 20
// Import all named exports
import * as math from './math.js';
console.log(math.add(2, 3)); // 5
console.log(math.PI); // 3.14159
// Import default export
import calculate from './math.js';
console.log(calculate('add', 10, 5)); // 15
// Import default + named exports
import calculate, { add, subtract } from './math.js';
Module Patterns
Utility Module
// utils.js
export const formatCurrency = (amount) => {
return `$${amount.toFixed(2)}`;
};
export const formatDate = (date) => {
return date.toLocaleDateString();
};
export const capitalize = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
// main.js
import { formatCurrency, capitalize } from './utils.js';
console.log(formatCurrency(99.99)); // "$99.99"
console.log(capitalize('hello')); // "Hello"
Config Module
// config.js
export const API_URL = 'https://api.example.com';
export const API_KEY = 'your-api-key';
export const TIMEOUT = 5000;
export default {
apiUrl: API_URL,
apiKey: API_KEY,
timeout: TIMEOUT,
features: {
darkMode: true,
notifications: false
}
};
// app.js
import config, { API_URL } from './config.js';
console.log(API_URL); // Direct access
console.log(config.features); // Object access
Class Module
// User.js
export default class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
export class Admin extends User {
constructor(name, email, role) {
super(name, email);
this.role = role;
}
manageUsers() {
return `${this.name} is managing users`;
}
}
// app.js
import User, { Admin } from './User.js';
const user = new User('Alice', 'alice@example.com');
const admin = new Admin('Bob', 'bob@example.com', 'superadmin');
Re-exporting Modules
// components/Button.js
export default function Button() { /*...*/ }
// components/Input.js
export default function Input() { /*...*/ }
// components/index.js (barrel file)
export { default as Button } from './Button.js';
export { default as Input } from './Input.js';
// Or
export * from './Button.js';
export * from './Input.js';
// app.js - Clean imports!
import { Button, Input } from './components/index.js';
Dynamic Imports
Load modules conditionally or on-demand:
// Static import (loaded immediately)
import { heavyFunction } from './heavy.js';
// Dynamic import (loaded when needed)
button.addEventListener('click', async () => {
const module = await import('./heavy.js');
module.heavyFunction();
});
// Conditional loading
if (userPreferences.darkMode) {
const { darkTheme } = await import('./themes/dark.js');
applyTheme(darkTheme);
}
// With error handling
try {
const math = await import('./math.js');
console.log(math.add(2, 3));
} catch (error) {
console.error('Failed to load module:', error);
}
Module Scope
Each module has its own scope:
// module.js
const privateVar = 'private'; // Not exported = not accessible outside
export const publicVar = 'public'; // Exported = accessible
export function accessPrivate() {
return privateVar; // Can access private vars inside module
}
// app.js
import { publicVar, accessPrivate } from './module.js';
console.log(publicVar); // "public" ✓
// console.log(privateVar); // Error! Not exported
console.log(accessPrivate()); // "private" (via function)
Using Modules in HTML
💡 Module Benefits
- • Encapsulation: Keep implementation details private
- • Reusability: Use code across multiple projects
- • Organization: Split large codebases into manageable files
- • Dependencies: Clear dependencies between files
- • Namespace: Avoid global scope pollution
⚠️ Common Issues
- • CORS errors when loading modules locally (use a dev server)
- • Forgetting
.jsextension in imports - • Circular dependencies (module A imports B, B imports A)
- • Mixing default and named exports inconsistently
✓ Best Practices
- • Use named exports for utilities, default for main class/component
- • Keep modules focused—one responsibility per file
- • Create barrel files (
index.js) for cleaner imports - • Use dynamic imports for code-splitting in large apps
- • Always include file extensions in imports