Modules: import and export

Named and default exports, and how a component file is wired together.

A module is one file. Nothing leaves a file until you export it, and nothing arrives until you import it.

Named exports

Put export in front of a function or a const and other files can use it. Import it by name, inside braces.

The sandbox below has two files. Use the tabs above the editor to switch between them.

import { formatTitle, isGood } from "./show.js";

const show = { title: "Severance", year: 2022, rating: 8.7 };

console.log("title:", formatTitle(show));
console.log("good?:", isGood(show));

The console reads title: Severance (2022) and good?: true. The braces are not an object — they are how you pick named exports out of a module.

Renaming an import

Two files can export the same name. as renames one of them, so both fit in one file. * as brings the whole module in under a single name.

import { formatTitle as label } from "./show.js";
import * as helpers from "./show.js";

const show = { title: "Andor", year: 2022, rating: 8.4 };

console.log("renamed:", label(show));
console.log("module: ", helpers.isGood(show));

// GOOD_RATING was never exported, so it is not here either.
console.log("private:", helpers.GOOD_RATING);

label is the same function under a new name. The last line prints private: undefined, because a namespace import only carries the exports.

The default export

A file can have one default export: its main thing. It is imported without braces, and the importing file picks the name.

import featured, { genres, formatTitle } from "./show.js";

console.log("title: ", featured.title);
console.log("label: ", formatTitle(featured));
console.log("genres:", genres.join(", "));

The default arrived as featured, so the first line prints title: Shogun. The named exports came in beside it, in braces, under their own names.

Because the importer picks the name, the same default can be called something different in every file that uses it. Named exports keep one name everywhere.

Imports are hoisted

Every import is resolved before any other line of the file runs. So a line above the import still works.

// This runs before the import line below, and still finds both names.
console.log("above the import:", formatTitle(featured));

import featured, { formatTitle } from "./show.js";

console.log("below the import:", formatTitle(featured));

Both lines print Severance (2022). Do not write it this way — imports go at the top, where readers look for them.

An import has to sit at the top level of the file. You cannot put one inside a function or an if.

This is the same import and export a later lesson uses to split the React app into one file per component.

Common mistakes

  • Braces on a default import. import { show } looks for a named export called show, not the default one.
  • No braces on a named import. The same mistake the other way round.
  • Expecting any name to be importable. Nothing leaves a file without export.

Exercise

/show.js has a formatTitle function, but /index.js cannot see it. Make it importable.

The console should read:

title: Severance (2022)
good?: true
import * as helpers from "./show.js";

const show = { title: "Severance", year: 2022, rating: 8.7 };

if (typeof helpers.formatTitle === "function") {
  console.log("title:", helpers.formatTitle(show));
} else {
  console.log("title: formatTitle is not exported yet.");
}

console.log("good?:", helpers.isGood(show));