CodingNic

Modules & Code Organization

Named Exports and Imports

Modules & Code Organization 20 min read

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:

text
<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.

javascript
// 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.

javascript
// 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.

javascript
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.

javascript
import { add as sum } from "./mathUtils.js";

console.log(sum(1, 1));
// 2

Try It

Imagine a file stringUtils.js with these exports:

javascript
// stringUtils.js
export function shout(text) {
  return text.toUpperCase() + "!";
}

export function reverse(text) {
  return text.split("").reverse().join("");
}

export const GREETING = "Hello";
  1. Write the import line that pulls in shout and GREETING from ./stringUtils.js, then call shout(GREETING) and log the result.
  2. Write an import * as line for the whole file, then call stringUtils.reverse("hello") and log the result.
  3. Import reverse, renamed to flip using as, and call flip("code").

Recap

  • A module is a file with its own private scope, loaded with <script type="module"> in the browser. Only exported values are visible to other files.
  • export in 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.
  • as renames 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.