Learning Objectives

  • Understand ES6 module syntax and benefits
  • Use named exports and imports
  • Apply default exports and imports
  • Master dynamic imports for code splitting
  • Organize code with module patterns

Why Modules?

Before ES6 modules, JavaScript had no native module system. Developers used global variables, IIFEs, CommonJS, or AMD. ES6 modules provide a clean, standardized solution.

Named Exports and Imports

Exporting

// utils.js
export const PI = 3.14159;

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

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

Importing

// app.js
import { PI, add, Calculator } from './utils.js';

console.log(PI); // 3.14159
console.log(add(2, 3)); // 5

const calc = new Calculator();
console.log(calc.multiply(4, 5)); // 20

Import All

import * as utils from './utils.js';

console.log(utils.PI);
console.log(utils.add(2, 3));
const calc = new utils.Calculator();

Rename Imports

import { add as sum, PI as pi } from './utils.js';

console.log(sum(2, 3));
console.log(pi);

Default Exports

Each module can have one default export:

// user.js
export default class User {
    constructor(name) {
        this.name = name;
    }
    
    greet() {
        return `Hello, I'm ${this.name}`;
    }
}

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

Importing Default

import User from './user.js';

const user = new User('John');
console.log(user.greet()); // "Hello, I'm John"
Note: You can name the default import anything:
import MyUser from './user.js'; // Works!
import AnyName from './user.js'; // Also works!

Mixing Default and Named Exports

// api.js
export default function fetchData() {
    return fetch('/api/data');
}

export const API_URL = 'https://api.example.com';
export const timeout = 5000;
// Import both
import fetchData, { API_URL, timeout } from './api.js';

console.log(API_URL);
const data = await fetchData();

Re-exporting

Aggregate exports from multiple modules:

// shapes/index.js
export { Circle } from './circle.js';
export { Square } from './square.js';
export { Triangle } from './triangle.js';

// Or re-export everything
export * from './circle.js';
export * from './square.js';
export * from './triangle.js';
// app.js
import { Circle, Square, Triangle } from './shapes/index.js';

const circle = new Circle(5);
const square = new Square(10);

Dynamic Imports

Load modules on demand for code splitting:

// 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 (userIsAdmin) {
    const adminModule = await import('./admin.js');
    adminModule.init();
}

// Feature detection
if ('IntersectionObserver' in window) {
    const { lazyLoad } = await import('./lazy-load.js');
    lazyLoad();
}

Lazy Loading Routes

const routes = {
    '/home': () => import('./pages/home.js'),
    '/about': () => import('./pages/about.js'),
    '/contact': () => import('./pages/contact.js')
};

async function loadRoute(path) {
    const module = await routes[path]();
    module.render();
}

// Usage
await loadRoute('/home');

Real-World Examples

Utility Functions Module

// utils/math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => b !== 0 ? a / b : 0;

// utils/string.js
export const capitalize = (str) => 
    str.charAt(0).toUpperCase() + str.slice(1);

export const slugify = (str) =>
    str.toLowerCase().replace(/\s+/g, '-');

export const truncate = (str, length) =>
    str.length > length ? str.slice(0, length) + '...' : str;

API Service Module

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

async function request(endpoint, options = {}) {
    const response = await fetch(`${BASE_URL}${endpoint}`, {
        headers: {
            'Content-Type': 'application/json',
            ...options.headers
        },
        ...options
    });
    
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    return response.json();
}

export async function fetchUsers() {
    return request('/users');
}

export async function fetchUser(id) {
    return request(`/users/${id}`);
}

export async function createUser(userData) {
    return request('/users', {
        method: 'POST',
        body: JSON.stringify(userData)
    });
}

export async function updateUser(id, userData) {
    return request(`/users/${id}`, {
        method: 'PUT',
        body: JSON.stringify(userData)
    });
}

export async function deleteUser(id) {
    return request(`/users/${id}`, {
        method: 'DELETE'
    });
}

Configuration Module

// config/index.js
const isDevelopment = process.env.NODE_ENV === 'development';

export const config = {
    apiUrl: process.env.API_URL || 'http://localhost:3000',
    timeout: 5000,
    retries: 3,
    debug: isDevelopment
};

export const endpoints = {
    users: '/api/users',
    posts: '/api/posts',
    comments: '/api/comments'
};

export default config;

Singleton Pattern with Modules

// database.js
class Database {
    constructor() {
        this.connection = null;
    }
    
    connect(connectionString) {
        if (!this.connection) {
            this.connection = createConnection(connectionString);
        }
        return this.connection;
    }
    
    query(sql) {
        if (!this.connection) {
            throw new Error('Not connected to database');
        }
        return this.connection.execute(sql);
    }
}

// Export a single instance
export default new Database();
// app.js
import db from './database.js';

await db.connect('mongodb://localhost:27017');
const users = await db.query('SELECT * FROM users');

Module Features

Strict Mode by Default

// Modules are automatically in strict mode
// No need for 'use strict';

// This will error in modules
undeclaredVariable = 5; // ReferenceError

Top-Level this is undefined

// In modules
console.log(this); // undefined

// In scripts
console.log(this); // window (in browsers)

Deferred Execution

// Modules are deferred by default
// Similar to