📦 ES6 Modules

Import and Export for Code Organization

What are Modules?

Modules allow you to organize code into reusable files with their own scope. Each module can export values and import from other modules.

📤 Export Syntax

Named Exports

// math.js - Export individual items
export const PI = 3.14159;
export const E = 2.71828;

export function add(a, b) {
    return a + b;
}

export function multiply(a, b) {
    return a * b;
}

export class Calculator {
    add(a, b) { return a + b; }
    subtract(a, b) { return a - b; }
}

// Or export all at once
const PI = 3.14159;
const E = 2.71828;

function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

export { PI, E, add, multiply };

// Export with rename
const privateFunction = () => {};
export { privateFunction as publicFunction };

Default Export

// user.js - One default export per module
export default class User {
    constructor(name) {
        this.name = name;
    }
}

// Or
class User {
    constructor(name) {
        this.name = name;
    }
}
export default User;

// Function as default
export default function greet(name) {
    return `Hello, ${name}!`;
}

// Object as default
export default {
    name: 'MyApp',
    version: '1.0.0',
    author: 'John Doe'
};

// Can mix default and named exports
export default function main() {}
export const helper = () => {};
export const VERSION = '1.0.0';

Re-exporting

// utils/index.js - Barrel exports
export { add, subtract } from './math.js';
export { capitalize, truncate } from './string.js';
export { default as User } from './user.js';

// Export all from module
export * from './constants.js';

// Export all with namespace
export * as math from './math.js';
// Usage: import { math } from './utils'
// math.add(1, 2)

// Re-export with rename
export { default as MainComponent } from './Component.js';
export { helper as utilityFunction } from './helpers.js';

📥 Import Syntax

Named Imports

// Import specific items
import { add, multiply } from './math.js';

console.log(add(2, 3));      // 5
console.log(multiply(2, 3)); // 6

// Import with rename
import { add as sum, multiply as product } from './math.js';

console.log(sum(2, 3));     // 5
console.log(product(2, 3)); // 6

// Import multiple items
import { PI, E, add, multiply, Calculator } from './math.js';

// Import all as namespace object
import * as math from './math.js';

console.log(math.PI);           // 3.14159
console.log(math.add(2, 3));    // 5
let calc = new math.Calculator();

// Empty import (just run the module)
import './setup.js';  // Executes setup code

Default Imports

// Import default export (any name)
import User from './user.js';
import MyUser from './user.js';  // Same thing, different name

let user = new User('John');

// Import default and named
import Calculator, { PI, E } from './math.js';

let calc = new Calculator();
console.log(PI);

// Import default with namespace
import { default as User, helper } from './user.js';

// Common pattern
import React, { useState, useEffect } from 'react';

Dynamic Imports

// Import on demand (returns Promise)
button.addEventListener('click', async () => {
    let module = await import('./heavy-module.js');
    module.doSomething();
});

// With destructuring
button.addEventListener('click', async () => {
    let { add, multiply } = await import('./math.js');
    console.log(add(2, 3));
});

// Conditional import
if (condition) {
    let module = await import('./feature.js');
    module.initialize();
}

// Dynamic module name
async function loadLocale(language) {
    let locale = await import(`./locales/${language}.js`);
    return locale.default;
}

let messages = await loadLocale('en');

// Error handling
try {
    let module = await import('./module.js');
    module.run();
} catch (error) {
    console.error('Failed to load module:', error);
}

🏗️ Module Patterns

Utility Module

// utils/string.js
export function capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}

export function truncate(str, length) {
    return str.length > length ? str.slice(0, length) + '...' : str;
}

export function slugify(str) {
    return str.toLowerCase().replace(/\s+/g, '-');
}

// Usage in another file
import { capitalize, truncate } from './utils/string.js';

let title = capitalize('hello world');
let short = truncate('This is a long text', 10);

Constants Module

// constants.js
export const API_URL = 'https://api.example.com';
export const API_KEY = 'your-api-key';
export const TIMEOUT = 5000;

export const STATUS = {
    PENDING: 'pending',
    SUCCESS: 'success',
    ERROR: 'error'
};

export const COLORS = {
    PRIMARY: '#007bff',
    SECONDARY: '#6c757d',
    SUCCESS: '#28a745',
    ERROR: '#dc3545'
};

// Usage
import { API_URL, STATUS, COLORS } from './constants.js';

console.log(STATUS.PENDING);
console.log(COLORS.PRIMARY);

Class Module

// user.js
export default class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }
    
    getFullName() {
        return this.name;
    }
    
    static fromJSON(json) {
        return new User(json.name, json.email);
    }
}

// Also export helper
export function validateEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

// Usage
import User, { validateEmail } from './user.js';

let user = new User('John', 'john@example.com');
if (validateEmail(user.email)) {
    console.log('Valid email');
}

Configuration Module

// config.js
const config = {
    development: {
        apiUrl: 'http://localhost:3000',
        debug: true
    },
    production: {
        apiUrl: 'https://api.example.com',
        debug: false
    }
};

const env = process.env.NODE_ENV || 'development';

export default config[env];

// Or with named exports
export const API_URL = config[env].apiUrl;
export const DEBUG = config[env].debug;

// Usage
import config from './config.js';
// or
import { API_URL, DEBUG } from './config.js';

📁 Module Organization

Barrel Exports (index.js)

// utils/index.js - Central export point
export { add, subtract, multiply, divide } from './math.js';
export { capitalize, truncate, slugify } from './string.js';
export { formatDate, parseDate } from './date.js';
export { default as User } from './user.js';
export { default as Product } from './product.js';

// Usage - cleaner imports
import { add, capitalize, formatDate, User } from './utils';

// Instead of
import { add } from './utils/math.js';
import { capitalize } from './utils/string.js';
import { formatDate } from './utils/date.js';
import User from './utils/user.js';

Feature Modules

// features/auth/index.js
import { login, logout, register } from './auth-service.js';
import { getUser, updateUser } from './user-service.js';
import AuthStore from './auth-store.js';

export { login, logout, register, getUser, updateUser, AuthStore };

// Or namespace export
import * as authService from './auth-service.js';
import * as userService from './user-service.js';

export { authService, userService };

// Usage
import { login, logout } from './features/auth';
// or
import { authService } from './features/auth';
authService.login(credentials);

Lazy Loading Modules

// app.js
class App {
    async loadDashboard() {
        let { Dashboard } = await import('./components/Dashboard.js');
        new Dashboard().render();
    }
    
    async loadSettings() {
        let { Settings } = await import('./components/Settings.js');
        new Settings().render();
    }
}

// Route-based loading
async function handleRoute(route) {
    switch (route) {
        case '/dashboard':
            let { Dashboard } = await import('./pages/Dashboard.js');
            return new Dashboard();
        case '/profile':
            let { Profile } = await import('./pages/Profile.js');
            return new Profile();
        default:
            let { NotFound } = await import('./pages/NotFound.js');
            return new NotFound();
    }
}

🌐 Browser Usage

HTML Script Tag












Import Maps




💡 Practical Examples

API Service Module

// services/api.js
const BASE_URL = 'https://api.example.com';

export async function get(endpoint) {
    let response = await fetch(`${BASE_URL}${endpoint}`);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
}

export async function post(endpoint, data) {
    let response = await fetch(`${BASE_URL}${endpoint}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
}

export async function put(endpoint, data) {
    let response = await fetch(`${BASE_URL}${endpoint}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
}

export async function del(endpoint) {
    let response = await fetch(`${BASE_URL}${endpoint}`, {
        method: 'DELETE'
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
}

// Usage in another file
import * as api from './services/api.js';

let users = await api.get('/users');
let newUser = await api.post('/users', { name: 'John' });

State Management Module

// store.js
class Store {
    constructor(initialState = {}) {
        this.state = initialState;
        this.listeners = [];
    }
    
    getState() {
        return this.state;
    }
    
    setState(updates) {
        this.state = { ...this.state, ...updates };
        this.listeners.forEach(listener => listener(this.state));
    }
    
    subscribe(listener) {
        this.listeners.push(listener);
        return () => {
            this.listeners = this.listeners.filter(l => l !== listener);
        };
    }
}

export default new Store({
    user: null,
    theme: 'light',
    notifications: []
});

// Usage
import store from './store.js';

// Subscribe to changes
let unsubscribe = store.subscribe(state => {
    console.log('State changed:', state);
});

// Update state
store.setState({ theme: 'dark' });

// Get current state
console.log(store.getState());

Router Module

// router.js
class Router {
    constructor() {
        this.routes = new Map();
        this.currentRoute = null;
        
        window.addEventListener('popstate', () => this.handleRoute());
    }
    
    register(path, handler) {
        this.routes.set(path, handler);
    }
    
    navigate(path) {
        history.pushState(null, '', path);
        this.handleRoute();
    }
    
    async handleRoute() {
        let path = window.location.pathname;
        let handler = this.routes.get(path);
        
        if (handler) {
            this.currentRoute = path;
            await handler();
        } else {
            this.handle404();
        }
    }
    
    handle404() {
        console.log('404 Not Found');
    }
}

export default new Router();

// app.js
import router from './router.js';

router.register('/', async () => {
    let { Home } = await import('./pages/Home.js');
    new Home().render();
});

router.register('/about', async () => {
    let { About } = await import('./pages/About.js');
    new About().render();
});

router.handleRoute();

Component System

// components/Component.js
export default class Component {
    constructor(selector) {
        this.el = document.querySelector(selector);
    }
    
    render() {
        this.el.innerHTML = this.template();
        this.attachEvents();
    }
    
    template() {
        return '';
    }
    
    attachEvents() {}
}

// components/TodoList.js
import Component from './Component.js';
import { get, post, del } from '../services/api.js';

export default class TodoList extends Component {
    constructor(selector) {
        super(selector);
        this.todos = [];
    }
    
    async initialize() {
        this.todos = await get('/todos');
        this.render();
    }
    
    template() {
        return `
            
    ${this.todos.map(todo => `
  • ${todo.text}
  • `).join('')}
`; } attachEvents() { this.el.querySelector('#addBtn').addEventListener('click', () => { this.addTodo(); }); this.el.querySelectorAll('.delete').forEach(btn => { btn.addEventListener('click', e => { let id = e.target.closest('li').dataset.id; this.deleteTodo(id); }); }); } async addTodo() { let input = this.el.querySelector('#todoInput'); let todo = await post('/todos', { text: input.value }); this.todos.push(todo); this.render(); } async deleteTodo(id) { await del(`/todos/${id}`); this.todos = this.todos.filter(t => t.id !== id); this.render(); } } // app.js import TodoList from './components/TodoList.js'; let todoList = new TodoList('#app'); todoList.initialize();

⚠️ Common Issues

// Must use file extension in browser
import { add } from './math';      // ❌ Error
import { add } from './math.js';   // ✅ Works

// Circular dependencies
// a.js
import { b } from './b.js';
export const a = 'A';

// b.js
import { a } from './a.js';  // May cause issues
export const b = 'B';

// Solution: restructure or use dynamic import

// Module scope
// Every module has its own scope
// math.js
let privateVar = 42;  // Not accessible outside
export const publicVar = 100;  // Accessible

// Modules are singletons
// store.js
let count = 0;
export function increment() { count++; }
export function getCount() { return count; }

// Both files share same instance
// a.js
import { increment } from './store.js';
increment();

// b.js
import { getCount } from './store.js';
console.log(getCount());  // 1

// CORS issues in browser
// Must serve files via HTTP server, not file://
// Use: python -m http.server or npm serve

🎯 Key Takeaways