cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

import & export

Learn how JavaScript modules share named exports, default exports, renamed imports, re-exports, live bindings, and strict module scope without leaking globals.

By the end you can
  • 01
    Choose export shapesImport named, default, namespace, and renamed exports without guessing.
  • 02
    Read module graphsUse barrel files and re-exports while understanding collisions and local bindings.
  • 03
    Trust live bindingsExplain why imports update, why import assignments fail, and why modules run in strict scope.

What import and export mean

A JavaScript module is a file with a private top level and an explicit public surface. Exports are the values a file chooses to share. Imports are the names another file uses to read those shared values.

This is the module version of a team saying, "Here is the part of my work you may use; everything else stays inside my file." That matters because large apps are not one long script. They are hundreds of small files that need clear contracts.

One-sentence definition

export publishes bindings from one module, and import creates live read-only bindings to them in another module.

If scope, strict mode, or hoisting feel fuzzy, keep the Scope & lexical environments, Strict mode, and Hoisting & the temporal dead zone lessons nearby. Modules combine all three ideas.

Named vs default exports

INTERACTIVE

A named export has a public name such as price or formatPrice. Importers ask for that exact export name inside braces. A default export is the special export whose name is default. When you import it, you choose the local name.

Real-life analogyA shop shelf and the house special

Imagine a tiny shop. The shelf has labels, so you ask for "tea" or "size" by name. The cafe also has one house special: you can order it and call it "lunch," "special," or "todayMeal" at your table.

In real life: Labeled items on a shelf
In JavaScript: Named exports such as tea and size
In real life: Asking for an item by label
In JavaScript: import { tea } from './shop.js'
In real life: The house special
In JavaScript: The default export, imported with any local name
In real life: A menu with several items
In JavaScript: A module may have many named exports and one default export

Where the analogy stops: A real shop can guess what you meant. JavaScript modules cannot. If you ask for a named export that does not exist, module linking fails before the body runs.

Named, default, namespace, and renamed exportsJavaScript
// shelf.jsexport const price = 5;export default "house special";export { price as cost }; // order.jsimport { price, cost } from "shelf";import special from "shelf";import * as shelf from "shelf";console.log(price, cost, special, shelf.default);

The namespace form, import * as shelf, gives you a module namespace object. It is read-only, non-extensible, and has a null prototype. You normally use it for clarity in demos or plugin-style APIs, not as a bag to mutate.

Export shapes lab: match the shelf to the order
Selected module codeJavaScript
export const tea = "green";export default "house special"; import { tea } from "shop";console.log(tea);
Sandboxed module runloading

The sandboxed module iframe is loading the selected code.

Try it yourself

Choose an exporter shape and an import form. The code runs in a sandboxed iframe with a real script type=module and an import map.

The lesson page never dynamically imports learner code. The iframe receives predefined module source as data URL entries in an import map, then reports validated results with postMessage.

Try a mismatch in the lab: choose "default export only" and import { tea } from 'shop';. The iframe shows the real browser error. Static imports are checked before ordinary statements run.

Renaming with as

CONCRETE

Renaming does not change the exporter. It only gives the importing file a local label. You use it when two modules export the same name, when a name would clash with a local variable, or when the imported name needs more context.

Real-life analogyPutting your own sticker on a product

Bring home two products both labeled "name," and you might put your own sticker on one: "userName." Renaming imports is the code version of that tidy habit.

In real life: The product says cost on the package
In JavaScript: The exporter publishes cost
In real life: You add a home sticker that says price
In JavaScript: import { cost as price }
In real life: The shop shelf label did not change
In JavaScript: Other importers still ask for cost

Where the analogy stops: A sticker can hide the old label. JavaScript keeps both ideas separate: the export name is still cost; your local binding is price.

Renaming imports and exportsJavaScript
// prices.jsconst cost = 12;export { cost as priceInDollars }; // cart.jsimport { priceInDollars as itemPrice } from "./prices.js";const price = "shown in the UI";console.log(itemPrice, price);

You can rename on export, on import, or both. Read from right to left: priceInDollars as itemPrice means "ask for the exported name priceInDollars, then call it itemPrice in this file."

Re-exports and barrel files

INTERACTIVE

A module can forward exports from other modules. A file that mostly re-exports from nearby files is often called a barrel, because importers can roll up to one place instead of visiting every file directly.

Real-life analogyA reception desk for module offices

In a building, reception can send you to payroll, security, or the design team. Reception makes the path easier; it does not do every job itself.

In real life: The reception desk
In JavaScript: index.js or another barrel file
In real life: Forwarding you to the math office
In JavaScript: export * from './math.js'
In real life: Choosing the right specialist
In JavaScript: export { shout } from './strings.js'
In real life: A department directory
In JavaScript: export * as strings from './strings.js'

Where the analogy stops: The receptionist does not become the specialist. A forwarding export does not create a local variable you can use inside the barrel.

A barrel that forwards a public APIJavaScript
// math.jsexport const add = (a, b) => a + b;export const name = "math"; // strings.jsexport const shout = (text) => text.toUpperCase();export const name = "strings"; // index.jsexport * from "math";export { shout } from "strings";export * as strings from "strings"; // app.jsimport { add, shout, strings } from "index";console.log(add(2, 3), shout("ok"), strings.name);

export * from forwards named exports, but not a module's default export. If two star exports contain the same name, that name becomes ambiguous and is not forwarded. Choose one explicitly with export { name } from './file.js'; when that happens.

Barrel lab: one reception desk, several offices
Barrel module and importerJavaScript
export * from "math";export { shout } from "strings";export * as strings from "strings"; import { add, shout, strings } from "index";console.log(add(2, 3), shout("ok"), strings.name);
Module graph resultloading

The sandboxed module iframe is loading the selected code.

Try it yourself

The barrel forwards public names from focused modules. Importers get one stable doorway without moving the implementation.

The iframe runs math, strings, index, and app as separate real modules. The parent accepts messages only from this sandbox.

Live bindings

STEP THROUGH

Imports are live bindings. That phrase means the importing module has a read-only view of the exporter's binding, not a frozen copy of the current value.

Real-life analogyA window, not a photo

A photo captures one moment. A window keeps showing what is happening now. Imported names behave like windows into exported bindings.

In real life: Looking through a window into a room
In JavaScript: Reading an imported binding
In real life: Someone in the room moves the chair
In JavaScript: The exporting module changes count
In real life: You see the chair in its new spot
In JavaScript: The importer reads the new value
In real life: You cannot move the chair through the glass
In JavaScript: The importer cannot assign to the import

Where the analogy stops: A real window shows objects. A module binding may point at any JavaScript value. If you copy the value into a local variable, that local variable is a snapshot.

Live binding lab: window or snapshot?
Step 0 of 7Ready
Your turn: follow the blue line

Imports are live views into exported bindings. Step through the read, the mutation, and the failed assignment.

Running in
  1. script
Next: line 2
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
// counter.jsexport function increment() {  count += 1;} // app.jsimport { count, increment } from "counter";console.log(count);increment();console.log(count);count = 10;
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Switch the importer line before stepping

Changing the line starts a fresh recorded run.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The exporter used export let count, so it may change count. The importer's name count is still read-only. To change the value, call an exported function such as increment() or expose another exported setter on purpose.

Circular imports and the temporal dead zone

Live bindings make cycles possible, but not magically safe. If a.js imports b from b.js, and b.js reads a before a.js initializes it, you can hit the same temporal dead zone rules from the Hoisting & TDZ lesson.

A tiny circular-import warningJavaScript
// a.jsimport { b } from "./b.js";export const a = "A"; // b.jsimport { a } from "./a.js";console.log(a); // may throw if a is still uninitializedexport const b = "B";

Module scope and strict mode

INTERACTIVE

Modules are not classic scripts with nicer syntax. They run in strict mode automatically, keep top-level declarations scoped to the module, make top-level this be undefined, and evaluate a module URL once.

Module scope lab: strict, private, evaluated once
Module scope checksJavaScript
// visits.jsconsole.log(this === undefined);let visits = 0;visits += 1;try { leaked = "global"; }catch (error) { console.log(error.name); }export const visitsSeen = visits;export const globalHasVisits = "visits" in globalThis; // app.jsimport { visitsSeen, globalHasVisits } from "visits";import { visitsSeen as again } from "visits";console.log(visitsSeen, again, globalHasVisits);
Real module factsloading

The sandboxed module iframe is loading the selected code.

Try it yourself
The iframe reloads on reset.

Top-level this is undefined; assigning an undeclared name throws; visits is not a global property; and importing the same URL twice shares the one evaluated module.

This is a browser module, not a Node-only simulation. The code runs in a sandboxed srcdoc iframe.

This is why modules fixed many old script problems from the var and the global object lesson. A top-level let visits in a module is private to that module. It does not become globalThis.visits.

Where you will use this

SORTER

Use named exports for a set of peer utilities, a default export for a file with one obvious main value, and re-exports for a small public doorway into a feature folder. Consistency matters more than fashion: pick a style your team can read at a glance.

Export forms at a glance
FormHow you import itBest use
Named exportimport { price } from './shop.js'Many public values with meaningful names.
Default exportimport anything from './shop.js'One obvious main value from the file.
Namespace importimport * as shop from './shop.js'Inspect a module as a read-only namespace object.
Re-exportexport { price } from './shop.js'Forward another module's public API.
API design sorter
  • A utility file has parsePrice, formatPrice, and taxRate.
  • A file represents one main Button component.
  • features/cart/index.js forwards the public cart API.
  • A validators file exposes isEmail and isStrongPassword.
  • A theme file exports the one design theme object used by an app.
  • ui/index.js gathers Button, Modal, and Toast from separate files.
Try it yourself
0 of 6 correct

Choose the export style that communicates each module design most clearly.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

Four module mix-ups
MistakeReality
Default means unnamedThe export name is literally default; the importer chooses a local name.
Imports are copiesImports are live read-only bindings to the exporter's binding.
A barrel owns the valuesA re-export forwards bindings; it does not make local variables.
Modules are like old scriptsModules are strict, scoped, deferred, and evaluated once per URL.
  • "Default exports are unnamed." The export name is default; the local import name is flexible.
  • "Import order can go anywhere because imports are hoisted." Import declarations are hoisted, but they must still be at the top level of a module.
  • "Namespace objects are normal objects." Module namespace objects are read-only views with a null prototype.
  • "A barrel is always better." Barrels help public APIs, but too many barrels can hide where code really lives.
  • "Modules only matter in browsers." Browsers and many other JavaScript runtimes use ES modules; hosts decide resolution details.

Practice exercises

4 EXERCISES
Exercise 1 · Warm-upPredict named plus default

Predict the exact output. This object stands in for a module namespace.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const module = { price: 5, default: "house special" };
const { price } = module;
const special = module.default;
console.log(price + " / " + special);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFix a renamed import

    The cart file wants a local variable named price, but the exporter only publishes cost. Type the fixed import clause.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // prices.js
    export const cost = 12;
    
    // cart.js
    import { price } from "./prices.js"; // fix this line

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeLive or copied?

      Write the two printed lines separated by a comma.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      let count = 0;
      function increment() { count += 1; }
      console.log(count);
      increment();
      console.log(count);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · ChallengeModule strictness

        In a module, what error name appears if code tries leaked = "global" without declaring leaked?

        Answer, then press Check. Spacing and letter case don’t matter.

          Check your understanding

          7 QUESTIONS
          Lesson quiz · 7 questionsScore: first tries count
          1. Question 1 of 7Which statement best describes a default export?

            Choose an answer to see the explanation.

          2. Question 2 of 7What does the named/default import example print?

            Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
            const module = { price: 5, default: "house special" };
            const { price } = module;
            const special = module.default;
            console.log(price + " / " + special);

            Choose an answer to see the explanation.

          3. Question 3 of 7Why write import { cost as price }?

            Choose an answer to see the explanation.

          4. Question 4 of 7What does the live binding model print?

            Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
            let count = 0;
            function increment() { count += 1; }
            console.log(count);
            increment();
            console.log(count);

            Choose an answer to see the explanation.

          5. Question 5 of 7What happens if an importing module assigns to an imported binding?

            Choose an answer to see the explanation.

          6. Question 6 of 7What does export * from do when two source modules export the same name?

            Choose an answer to see the explanation.

          7. Question 7 of 7Which module-scope statement is true?

            Choose an answer to see the explanation.

          Key takeaways

          • Named exports are imported by their exported names; a default export is the export named default.
          • as renames locally without changing the exporter.
          • Re-exports forward bindings; star exports omit ambiguous duplicate names.
          • Imports are live read-only bindings, not assignable local variables.
          • Modules are strict, scoped, and evaluated once per module URL.

          Final definition.
          import and export connect module files through explicit, live bindings while keeping each module's private scope intact.

          Up next: Dynamic import & top-level await.

          CompleteFrontend Clear concepts. Working examples.