Named Exports and Imports
Objectives
By the end of this chapter, you should be able to:
- Explain what an ES module is and how it’s loaded in the browser
- Share a value from one file with
export - Use a value from another file with
import - Rename an import with
as
💡 Why this matters: Every project so far in this course has lived in one file. That doesn’t scale, real projects split code across dozens or hundreds of files. Modules are how those files share code with each other.
What Makes a File a Module
A plain <script> tag runs a file in the global scope, everything it declares is visible to every other script on the page. A module is different: each module file has its own private scope, and the only way to share something from it is to explicitly export it. In the browser, you opt into this with type="module" on the script tag:
<script type="module" src="main.js"></script>
Once a file is loaded this way, it can use import and export, and so can any file it imports.
Exporting a Value
Add export in front of a function, variable, or constant to make it available to other files.
// mathUtils.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
mathUtils.js now has three named exports: add, subtract, and PI. Anything not marked export stays private to this file, invisible to anything that imports from it.
Importing a Value
import { name } from "./path" pulls a named export into the current file, using the exact name it was exported with.
// main.js
import { add, subtract, PI } from "./mathUtils.js";
console.log(add(2, 3));
// 5
console.log(subtract(5, 2));
// 3
console.log(PI);
// 3.14159
The path has to start with ./, ../, or a full URL. Unlike require() in older Node code, a browser module import can’t take a bare name like "mathUtils.js" and go looking for it, browsers have no built-in package lookup. Leaving off the ./ is a common mistake, and the browser will refuse to resolve it rather than guess what you meant.
Importing Everything at Once
import * as name from "./path" imports every named export from a file as properties of one object, useful when you need several exports, or don’t want to list each one individually.
import * as mathUtils from "./mathUtils.js";
console.log(mathUtils.add(10, 5));
// 15
console.log(mathUtils.PI);
// 3.14159
Renaming an Import
as renames an import, useful when the original name is too generic, or clashes with something else already in the file.
import { add as sum } from "./mathUtils.js";
console.log(sum(1, 1));
// 2
Try It
Imagine a file stringUtils.js with these exports:
// stringUtils.js
export function shout(text) {
return text.toUpperCase() + "!";
}
export function reverse(text) {
return text.split("").reverse().join("");
}
export const GREETING = "Hello";
- Write the
importline that pulls inshoutandGREETINGfrom./stringUtils.js, then callshout(GREETING)and log the result. - Write an
import * asline for the whole file, then callstringUtils.reverse("hello")and log the result. - Import
reverse, renamed toflipusingas, and callflip("code").
Recap
- A module is a file with its own private scope, loaded with
<script type="module">in the browser. Onlyexported values are visible to other files. exportin front of a function, variable, or constant creates a named export.import { name } from "./path"imports a specific named export.import * as name from "./path"imports all of them as one object.asrenames an import, useful for clarity or to avoid a naming clash.
Next lesson: default exports, for when a module has one main thing to share.