Modules

Split code into files. Import what you need, export what others can use.

Named Exports

Export multiple values from a module by name.

// math.js — export individually:
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;
}

// Or export all at once at the bottom:
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 };

// Rename on export:
export { add as sum, multiply as product };
// Importing named exports:
import { PI, add } from "./math.js";

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

// Rename on import:
import { add as sum } from "./math.js";
sum(2, 3); // 5

// Import everything as a namespace:
import * as math from "./math.js";
math.PI;        // 3.14159
math.add(2, 3); // 5

Default Exports

One main export per module — imported without braces.

// User.js — one default export per file:
export default class User {
  constructor(name) {
    this.name = name;
  }
}

// Or export separately:
class User { /* ... */ }
export default User;

// Functions:
export default function fetchData(url) {
  return fetch(url).then(r => r.json());
}

// Even expressions:
export default [1, 2, 3];
// Importing defaults — no braces, any name works:
import User from "./User.js";          // ✓
import MyUser from "./User.js";        // ✓ same thing, different name
import Whatever from "./User.js";      // ✓ name doesn't matter

// Combine default + named:
import React, { useState, useEffect } from "react";
import axios, { AxiosError } from "axios";

// ⚠️ Only ONE default per module:
export default class A {}
export default class B {} // ❌ SyntaxError

Named exports

Multiple per file

Must use exact name

Better for utilities

Default export

One per file

Any import name

Better for components

Import Patterns

All the ways to import modules.

// Named import:
import { useState, useEffect } from "react";

// Default import:
import React from "react";

// Both:
import React, { useState } from "react";

// Namespace (all as object):
import * as utils from "./utils.js";

// Rename:
import { render as renderDOM } from "react-dom";

// Side-effect only (no values imported):
import "./styles.css";
import "./polyfills.js";

// Re-export (barrel files):
// index.js
export { Button } from "./Button.js";
export { Input } from "./Input.js";
export { Modal } from "./Modal.js";
// Now consumers: import { Button, Input, Modal } from "./components";

// Re-export everything:
export * from "./math.js";
export { default } from "./User.js";

Dynamic Imports

Load modules on demand — code splitting.

// import() returns a Promise:
const module = await import("./heavy-module.js");
module.doSomething();

// Conditional loading:
if (user.isAdmin) {
  const { AdminPanel } = await import("./AdminPanel.js");
  renderAdmin(AdminPanel);
}

// Lazy load on event:
button.addEventListener("click", async () => {
  const { openModal } = await import("./modal.js");
  openModal();
});

Dynamic import() loads modules at runtime. The browser downloads the code only when the import is executed — perfect for code splitting.

1 / 2

Module Patterns

Common conventions and best practices.

// 1. Barrel file (re-export pattern):
// components/index.js
export { Button } from "./Button";
export { Input } from "./Input";
export { Card } from "./Card";
// Import: import { Button, Input, Card } from "./components";

// 2. Module-level state (singleton):
// counter.js
let count = 0;
export const increment = () => ++count;
export const getCount = () => count;
// Same instance shared across all importers

// 3. Constants file:
// config.js
export const API_URL = "https://api.example.com";
export const MAX_RETRIES = 3;
export const TIMEOUT = 5000;

// 4. Type-only imports (TypeScript):
import type { User, Post } from "./types";
import { type Config, loadConfig } from "./config";

Module features:

• Strict mode by default (no need for "use strict")

• Own scope (no global pollution)

• Executed once (cached on subsequent imports)

• Static structure (imports hoisted, analyzable)

• Live bindings (exports update when source changes)

FAQ

Common questions about ES modules.