As a JavaScript project grows beyond a single file, you need a way to split code across files and share functions, classes, and values between them. ES6 (ES2015) introduced a standardized module system built around two keywords, export and import, replacing a mess of older, non-standard approaches (global script tags, CommonJS's require(), various bundler-specific formats).

Named Exports

A module can export as many named values as it wants. Each one is imported by its exact name (you can rename it on the way in if you want):

// math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export const PI = 3.14159;
// main.js
import { add, subtract, PI } from "./math.js";

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

// Renaming on import with 'as'
import { add as sum } from "./math.js";
console.log(sum(10, 20)); // 30

Default Exports

A module can also have one default export — typically used when a file's whole purpose is to export a single main thing, like a class or a component:

// user.js
export default class User {
  constructor(name) {
    this.name = name;
  }
}
// main.js — no curly braces, and you can name it anything on import
import User from "./user.js";

const u = new User("Alex");
console.log(u.name); // Alex

A single file can mix a default export with named exports, though it's common practice to keep a module focused on one main thing plus a few closely related helpers.

Using Modules in the Browser

To actually use import/export in a browser (rather than through a bundler like Vite or webpack), mark the script tag as a module:

<script type="module" src="main.js"></script>

This matters because module scripts behave differently from classic scripts in a few ways: they're automatically deferred (they don't block HTML parsing), they run in strict mode by default, and each module has its own scope — top-level variables don't leak onto the global window object the way they do in a plain script. For a refresher on how adding scripts works in general, see adding JavaScript to HTML.

Why Modules Matter

Before ES6 modules, keeping a multi-file JavaScript project organized meant carefully ordering <script> tags and relying on global variables to share data between files — fragile, hard to trace, and prone to naming collisions. Modules make dependencies explicit: reading the import lines at the top of a file tells you exactly what it relies on, and each module's internal variables stay private unless explicitly exported.