Modules
As programs grow, keeping everything in one file becomes unmanageable. Modules let you split code into files, each with its own private scope, and explicitly export what other files may import. They're supported natively by all modern browsers and Node.js.
Exporting and importing
export const PI = 3.14159; export function circleArea(r) { return PI * r * r; } function secretHelper() { return "not exported, so private to this file"; }
import { circleArea, PI } from "./math.js"; console.log(circleArea(2)); console.log(PI);
Anything not exported stays private to its module — secretHelper can't be imported. Import paths for your own files are relative (./, ../) and, in browsers and Node, include the .js extension.
Using modules in a page
<script type="module" src="main.js"></script>
type="module" tells the browser to treat the file as a module. Module scripts are deferred automatically, run in strict mode, and have their own top-level scope instead of creating globals. They must be served over HTTP — opening the HTML file directly from disk (file://) blocks module imports, so use a local server like the VS Code Live Server extension or npx serve.
Default exports
A module can have one default export, imported without braces under any name you choose:
export default function log(message) { console.log(`[${new Date().toISOString()}] ${message}`); }
import log from "./logger.js"; import writeLog from "./logger.js"; // same thing, different local name log("App started");
Named exports are often preferred, because the name stays consistent across files and editors can auto-import them reliably.
Renaming and namespaces
import { circleArea as area } from "./math.js"; import * as math from "./math.js"; area(3); math.circleArea(3); console.log(math.PI);
as renames an import to avoid clashes. import * as name gathers all named exports into one object.
Re-exporting
A folder can have an index.js that gathers exports from several files, so other code imports from one place:
export { formatDate } from "./dates.js"; export { formatMoney, parseMoney } from "./money.js"; export * from "./strings.js";
import { formatDate, formatMoney } from "./utils/index.js";
Dynamic imports
import() loads a module on demand and returns a promise — so code that's only sometimes needed doesn't slow down the first page load:
document.querySelector("#chart-tab").addEventListener("click", async () => { const { drawChart } = await import("./charts.js"); drawChart(document.querySelector("#chart")); });
Modules run once
However many files import a module, its code runs only once and every importer shares the same instance. That makes a module a natural place for shared state:
let count = 0; export const increment = () => ++count; export const current = () => count;
If a.js and b.js both import counter.js and call increment(), they're updating the same count.
Other module features
- Top-level
await— modules can useawaitoutside any function, for example to load configuration before exporting it. import.meta.url— the module's own URL, useful for loading files next to it.- Import maps — a
<script type="importmap">lets browsers resolve bare names likeimport confetti from "confetti"to a URL. - JSON modules —
import data from "./data.json" with { type: "json" }.
Modules in Node.js and npm
In Node.js, a .mjs file, or any .js file in a project whose package.json has "type": "module", uses this same syntax. Packages from npm are imported by name:
import express from "express"; import { readFile } from "node:fs/promises";
You'll also meet the older CommonJS format in Node code, which uses require() and module.exports:
const fs = require("fs"); module.exports = { helper() {} };
New code should use ES modules (import/export), which work in both browsers and Node.
Bundlers
Real-world projects usually run their modules through a build tool such as Vite, esbuild or webpack. These bundle many modules into a few optimised files, remove unused code ("tree-shaking"), and let you import npm packages and even CSS directly. During development, Vite serves native modules almost instantly.