Section 5 · Writing Real Programs
Modules: import and export
15 min3 min readSpot the bug
Lesson at a glance
Split a growing program into modules with export and import, load them in the browser with type=“module” from a dev server, and organise Pocket into files with clear jobs.
- 1A module is a file with its own scope
- 2Loading modules in the browser
- 3Modules in Node.js
- 4Organising Pocket
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Pocket now has money helpers, validation, storage, rendering, event handlers and network code. In one file that is several hundred lines, every function can see every variable, and changing one part means scrolling past all the others. Real programs are split into files, each with one job, and JavaScript’s way of doing that is modules.
A module is a file with its own scope
In a module, top-level variables and functions are private to that file. To share something, you export it. To use something from another module, you import it:
export function toCents(dollars) {
return Math.round(dollars * 100);
}
export function formatMoney(cents, symbol = "$") {
return `${symbol}${(cents / 100).toFixed(2)}`;
}
const SECRET_ROUNDING_NOTE = "not exported, so only this file can see it";import { toCents, formatMoney } from "./money.js";
console.log(formatMoney(toCents(12.2)));The curly braces list the named exports you want. The path starts with ./, meaning “relative to this file”, and in the browser it includes the .js extension. SECRET_ROUNDING_NOTE cannot be imported at all; that is the point. Each module decides what it offers, and everything else is hidden, much like the closures from section 2, at file level.
A few more forms you will see:
import { formatMoney as fmt } from "./money.js"; // rename on import
import * as money from "./money.js"; // everything, as money.toCents etc.There is also a default export, one per file, imported without braces and under any name you like: export default function renderApp() {} and then import renderApp from "./ui.js". Many teams prefer named exports, and so does this course: a named export has the same name in every file that uses it, so search and automatic renaming in your editor work reliably.
Loading modules in the browser
The entry file is loaded with type="module", and it imports everything else:
<script type="module" src="./src/main.js"></script>Module scripts behave a little differently from the classic scripts you have used so far. They are deferred automatically, so the DOM is ready when they run. They run in strict mode, which turns some silent mistakes into errors. And each module runs once, however many files import it, so a module-level variable is shared by all its importers.
In practice, most projects use a build tool such as Vite. It serves your modules during development, lets you import packages from npm by name (import { something } from "some-package"), and at build time bundles everything into a few optimised files. The import and export syntax you write is exactly the same.
Modules in Node.js
Node.js supports the same syntax. A file is treated as an ES module when the nearest package.json contains "type": "module", or when the file name ends in .mjs. (Recent Node.js versions also detect import syntax in a plain .js file, but setting "type" explicitly is clearer.) You will also meet the older Node.js system, CommonJS, which uses require() and module.exports; it is still common in existing projects, but new code uses import and export.
Organising Pocket
A good split follows jobs, and keeps the parts that touch the outside world separate from the pure logic:
money.js:toCents,formatMoney. Pure.expenses.js:addExpense,removeExpense,totalCents,totalsByCategory,validateExpense. Pure: data in, data out.storage.js:loadExpenses,saveExpenses. The only file that knows aboutlocalStorage.ui.js:renderExpensesand other DOM code.main.js: the entry point. Loads data, connects event listeners, and calls the others.
The rule behind the split: dependencies point from the outside in. main.js knows about everything; expenses.js knows about nothing but data. That means you can test, or reuse in Node.js, the core logic without a browser, and you can swap localStorage for a server later by changing one file.
This course’s playground runs one file at a time, so the practice below is about reading module code. In the next lesson you will meet another way to organise code, classes, which group data with the functions that act on it.
Key takeaways
- A module is a file with its own scope; nothing in it is visible to other files unless it is exported.
- Prefer named exports (
export function formatMoney), which keep the same name everywhere and are easy to search for. - In the browser, load the entry file with
<script type="module">, use relative paths with the.jsextension, and serve the files over http. - In Node.js, files are ES modules when the nearest
package.jsonhas"type": "module"or the file ends in.mjs. - [object Object]