Skip to content
aviral gupta

// I3.2 · ~34 min · Intermediate

Named and default exports, as and import *

After this lesson you choose between named exports and a default export, import both correctly, rename with as, and build an index module that gathers other modules.

Lesson 2 of 5 in I3 Modules, Node.js and npm

You will be able to

  • Choose between named exports and one default export, and import each with the right syntax
  • Rename with as, and import a whole module as a namespace object with import * as
  • Gather several modules in an index file with export … from, and know that export * skips the default
  1. Warm-up · Activity 1 of 7

    Warm-up from lesson I3.1. add has no export. What does main.js print?

    // math.js
    function add(a, b) {
      return a + b;
    }
    
    // main.js
    import {add} from "./math.js";
    console.log(add(1, 2));
  2. Predict · Activity 2 of 7

    Predict before you read on. main.js imports add without braces. What does it print?

    // math.js
    export function add(a, b) {
      return a + b;
    }
    export default function square(x) {
      return x * x;
    }
    
    // main.js
    import add from "./math.js";
    console.log(add(3, 4));
  3. Practice · Activity 3 of 7

    Fill in the word that makes greet the module’s default export.

    export ____ function greet(name) {
      return "Hello, " + name;
    }
    exportfunction greet(name) {
  4. Practice · Activity 4 of 7

    Match each import or export to what it does.

  5. Practice · Activity 5 of 7

    What does main.js print?

    // math.js
    export function add(a, b) {
      return a + b;
    }
    export default function square(x) {
      return x * x;
    }
    
    // main.js
    import * as math from "./math.js";
    console.log(Object.keys(math));
  6. Brain teaser · Activity 6 of 7

    Brain teaser. index.js re-exports everything from math.js. What does main.js print?

    // math.js
    export function add(a, b) {
      return a + b;
    }
    export default function square(x) {
      return x * x;
    }
    
    // index.js
    export * from "./math.js";
    
    // main.js
    import square from "./index.js";
    console.log(square(5));
  7. Apply · Activity 7 of 7

    Mini-task: build a shapes folder. shapes/circle.js has a named export area(r), rounded to 2 decimals; shapes/square.js has a default export for the area of a square. Write shapes/index.js so that main.js can import circleArea and squareArea, both by name, from "./shapes/index.js". Print the area of a circle with radius 1 and of a square with side 3.

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

Every kind of import and export

text.js has two named exports and a default export. numbers.js has one named export. utils.js is an index module: it passes on the named exports of text.js, renames round2 to round, and turns text.js’s default into a named slugify. main.js imports the default under its own name, renames capitalize, and takes utils.js as a namespace object.

main.js

import makeSlug, {capitalize as cap} from "./text.js";
import * as utils from "./utils.js";

console.log(makeSlug("Named And Default"));
console.log(cap("module"));
console.log(Object.keys(utils));
console.log(utils.round(3.14159), utils.shout("hi"));

text.js

export function capitalize(word) {
  return word[0].toUpperCase() + word.slice(1);
}

export function shout(text) {
  return text.toUpperCase() + "!";
}

// The one default export of this file.
export default function slugify(title) {
  return title.toLowerCase().split(" ").join("-");
}

numbers.js

export function round2(n) {
  return Math.round(n * 100) / 100;
}

utils.js

// An index module: it gathers features from other files.
export * from "./text.js"; // every named export, but not the default
export {round2 as round} from "./numbers.js";
export {default as slugify} from "./text.js";

Run it with

node main.js

Output

named-and-default
Module
[ 'capitalize', 'round', 'shout', 'slugify' ]
3.14 HI!
  • main.js named the default export makeSlug; in text.js it is called slugify. Any name works for a default.
  • utils.js has slugify as a named export: export {default as slugify} turned the default into one.
  • The namespace has no round2: the index only passed it on renamed, as round.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Exercises

Exercise 1 of 2

One default, two named

main.js should export formatPrice(cents) as its default export, which turns 1999 into "19.99 EUR", and two named exports: CURRENCY ("EUR") and parsePrice(text), which turns "19.99" into 1999. The starter exports formatPrice by name, so a file that imports the default gets nothing. Make it the default export.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    Run the tests: the module has no default export. Which word turns an export into the default?

  2. Hint 2

    A module has at most one default export; the other two stay named.

  3. Hint 3

    export default function formatPrice(cents) { … }

Show a solution

One way to solve it. Yours can look different and still pass the checks.

export const CURRENCY = "EUR";

export default function formatPrice(cents) {
  return (cents / 100).toFixed(2) + " " + CURRENCY;
}

export function parsePrice(text) {
  return Math.round(Number(text) * 100);
}
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

export const CURRENCY = "EUR";

export function formatPrice(cents) {
  return (cents / 100).toFixed(2) + " " + CURRENCY;
}

export function parsePrice(text) {
  return Math.round(Number(text) * 100);
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import * as prices from './main.js';

test('The default export formats cents', () => {
  assert.equal(typeof prices.default, 'function', 'main.js has no default export function');
  const got = prices.default(1999);
  assert.equal(got, '19.99 EUR', `the default export gave ${got}`);
});

test('CURRENCY and parsePrice are named exports', () => {
  assert.equal(prices.CURRENCY, 'EUR', `CURRENCY is ${prices.CURRENCY}`);
  assert.equal(prices.parsePrice('19.99'), 1999, 'parsePrice should turn 19.99 into 1999');
});

test('formatPrice is the default, not also a named export', () => {
  assert.equal('formatPrice' in prices, false, 'formatPrice should be exported only as the default');
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Exercise 2 of 2

An index module

text.js and numbers.js are given. main.js is the index module of a small library: it should export exactly capitalize (named, from text.js), round (numbers.js’s round2, renamed) and slugify (text.js’s default, as a named export). The starter uses export * twice, which passes on round2 under its old name and leaves out the default.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    Run the tests: the index exports round2 and no slugify. Which form re-exports one name and can rename it?

  2. Hint 2

    export {round2 as round} from "./numbers.js"; renames while passing it on.

  3. Hint 3

    A default is passed on by its name default: export {default as slugify} from "./text.js";

Show a solution

One way to solve it. Yours can look different and still pass the checks.

export {capitalize} from "./text.js";
export {round2 as round} from "./numbers.js";
export {default as slugify} from "./text.js";
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

export * from "./text.js";
export * from "./numbers.js";

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import * as lib from './main.js';

test('The index exports exactly capitalize, round and slugify', () => {
  const names = Object.keys(lib);
  assert.deepEqual(names, ['capitalize', 'round', 'slugify'], `main.js exports ${JSON.stringify(names)}`);
});

test('The re-exported functions work', () => {
  assert.equal(lib.capitalize('ada'), 'Ada', 'capitalize should give Ada');
  assert.equal(lib.round(3.14159), 3.14, 'round should round to 2 decimals');
  assert.equal(lib.slugify('Hello World'), 'hello-world', 'slugify should give hello-world');
});

text.js

export function capitalize(word) {
  return word[0].toUpperCase() + word.slice(1);
}

export default function slugify(title) {
  return title.toLowerCase().split(" ").join("-");
}

numbers.js

export function round2(n) {
  return Math.round(n * 100) / 100;
}

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Common mistakes

Importing a named export without braces

import add from "./math.js";
console.log(add(1, 2));

What Node.js prints

SyntaxError: The requested module './math.js' does not provide an export named 'default'

Why, and the fix

Without braces, import asks for the default export, and math.js has none. Import the named export in braces: import {add} from "./math.js". Or, if add is meant to be the main thing the module offers, export it with export default.

export default with a const declaration

export default const VAT = 0.19;
console.log("loaded");

What Node.js prints

SyntaxError: Unexpected token 'const'

Why, and the fix

export default takes an expression, a function or a class, not a variable declaration. Declare first, then export: const VAT = 0.19; export default VAT;. Or write the value directly: export default 0.19;. Usually a named export, export const VAT = 0.19;, is clearer.

Assigning to a property of a namespace object

import * as math from "./math.js";
math.add = (a, b) => a - b;
console.log(math.add(5, 2));

What Node.js prints

TypeError: Cannot assign to read only property 'add' of object '[object Module]'

Why, and the fix

A namespace object is sealed and its properties are read-only: it is a view of the module’s exports, which only the module can change. To use a different function, declare your own: const subtract = (a, b) => a - b;.

JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

Named exports and one default

A module can have many named exports and at most one default export. Named exports are imported in braces, by their exact names: import {add} from "./math.js". The default export is imported without braces, under any name you like: import square from "./math.js". Both can share one line, default first: import square, {add} from "./math.js". export default takes a function, a class or an expression such as 42, never a declaration with const. Named exports keep names the same in every file, which helps when you search a project.

Renaming and namespaces

as renames on either side: import {add as plus} from "./math.js" gives you plus, and add is then not defined in that file. import * as math from "./math.js" gives a namespace object: every export is a property, math.add, and the default export is math.default. The namespace is sealed and its properties are read-only, so math.add = null throws a TypeError. It is also live: it always shows the module’s current values.

Index modules

A folder of modules often has an index.js that gathers them, so other files import from one place. export {area as circleArea} from "./circle.js" re-exports one name, renamed if you like. export {default as squareArea} from "./square.js" turns a default into a named export. export * from "./text.js" re-exports every named export, but not the default. Re-exported names are not variables inside index.js itself. If two export * lines bring the same name, importing that name fails.

Sources

Last reviewed October 5, 2026