skip to content

Import and Export Forms

The concrete syntax for getting values in and out of a module, and when a default export helps or hurts. Interviewers ask you to contrast named and default exports and to say what `export * from` actually re-exports.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

4

In ES modules, what is the difference between a named export and a default export, and how does the import syntax differ for each?

level: juniorimportance: must knowfreq 80%

answer

  1. one export table, two spellings
  2. default is just a name
  3. importer picks the default's name
  4. braces must match the exported name
  5. at most one default per module

basics

~20 s

A named export binds a value to a specific export name that importers must match exactly, using braces: import { parse } from './m.js'. A default export registers the value under the name "default", and the importer may bind it to any identifier it likes.

solid answer

~50 s

Both are entries in the same export table; the only difference is the name. `export const parse = ...` or `export { parse }` registers the export name `parse`, and an importer must spell it identically inside braces — `import { parse } from './m.js'` — optionally renaming with `import { parse as p }`. `export default value` registers the export under the reserved name `default`, and the importer writes it without braces and chooses the local name freely: `import anythingAtAll from './m.js'`. Because it is just a name, `import { default as parse }` is exactly equivalent to the default import, and a namespace import exposes it as `ns.default`. A module may have many named exports but at most one default, and `export default` takes a function or class declaration or an expression — so `export default const x = 1` is a syntax error.

go deeper

for a junior

Be ready to write both import shapes from memory and say which needs braces. Know that a default import name is yours to choose, while a named import must match the exported spelling exactly.

for a middle

Explain that export default just registers the export name "default", and show the equivalences: import { default as x }, export { x as default }, and ns.default after a namespace import. Know which combined import forms are grammatically legal.

for a senior

Show that unresolved import names are reported at link time rather than surfacing as undefined at runtime, and explain what that buys a codebase in early failure. Be able to justify a house rule on default exports for shared modules.

for a principal

Own the convention for the organisation: whether shared packages expose named exports only, how that interacts with barrel files and public API stability, and how you enforce it with lint rules rather than review habit.

## Two spellings, one mechanism Every ES module carries a table of export entries that map an **export name** (a string) to a binding inside the module. Named exports use whatever identifier you pick. `export default` is not a second mechanism bolted on beside it — it simply registers an entry under the literal name `"default"`. Almost every behaviour below falls out of that one fact. ## The named-export forms There are two spellings, and they are interchangeable: ```js // geometry.js export const PI = 3.14159; // declaration and export together export function area(r) { return PI * r * r; } export class Circle {} const helper = (x) => x * 2; export { helper }; // export clause, written separately export { area as circleArea }; // the same value under a second name ``` The export clause form is useful when you want the export list in one visible place, or when you want to expose a binding under a different public name than its internal one. A single binding may be exported under several names; each `as` adds another entry pointing at the same binding. ## The default form ```js // logger.js export default function log(msg) { console.log(msg); } export const LEVEL = 'info'; // defaults and named exports coexist ``` `export default` accepts a function declaration, a class declaration, or **an expression** — so `export default { retries: 3 }` and `export default 42` are both legal, while `export default const x = 1;` is a SyntaxError because a `const` declaration is not an expression. A module may declare at most one default; a second one is a SyntaxError. The clause equivalent is `export { helper as default }`. ## The import forms ```js import log from './logger.js'; // default, name chosen by the importer import whateverIWant from './logger.js'; // identical to the line above import { LEVEL } from './logger.js'; // named, must match exactly import { LEVEL as level } from './logger.js'; // named, renamed locally import log, { LEVEL } from './logger.js'; // default plus named import log, * as logger from './logger.js'; // default plus namespace import * as logger from './logger.js'; // logger.LEVEL, logger.default import './setup.js'; // no bindings, side effects only ``` One combination is *not* allowed: a namespace import cannot sit beside a named-import list, so `import { LEVEL }, * as logger from './logger.js'` is a SyntaxError. Note also that `import { default } from './logger.js'` fails, because `default` is a reserved word and cannot be a binding name — you must write `import { default as log }`. ## Mismatched names fail structurally, not silently Because imports are resolved before any module code runs, a named import that does not correspond to an export is reported as a SyntaxError at link time, before the importing module executes a single statement. It does not quietly produce `undefined`, and it does not wait until you call the value. The same applies to a default import from a module that declares no default: `default` is just a name, and an unresolvable name is an error. Candidates who expect `undefined` are carrying over intuition from destructuring a plain object. ## Which to reach for Named exports keep the public name of a value under the author's control: the importer must spell it correctly, editors can auto-import and rename it across a project, and the name is greppable. A default export lets a file present a single obvious thing — a component, a class, a configuration object — without repeating its name, but the naming decision moves to each importer, so the same value ends up called different things in different files. Many teams standardise on named exports for shared libraries for exactly that reason, while single-artifact files keep a default. Mixing both in one module is legal and common, but it forces consumers to use both import shapes for one file. ## A quick self-check Given `export default class Button {}` in `button.js`, `import Btn from './button.js'` and `import { default as Btn } from './button.js'` bind the same class, and `import * as m from './button.js'` gives an object whose `default` property is that class. If you can state that, you have the model right.

  • Can a module have both a default export and named exports at the same time?
    Yes. `export default class Button {}` alongside `export const SIZES = [...]` is completely legal, and a consumer can pull both in one statement: `import Button, { SIZES } from './button.js'`. The only cap is that there can be at most one default entry per module; named exports are unlimited.
  • Why is `export default const config = {}` a syntax error when `export const config = {}` is fine?
    Because `export default` is followed by a function declaration, a class declaration, or an expression — never a variable declaration. `const config = {}` is a declaration, so it does not fit the grammar. Write `const config = {}; export default config;`, or just `export default {}` if the local name is not needed.
  • What does `import './polyfills.js'` with no bindings actually do?
    It is a side-effect-only import: the module is resolved, linked and evaluated exactly once, but nothing is bound in the importing scope. It is the right form for a module whose whole purpose is to run — registering a polyfill, installing a global handler, importing a stylesheet in a bundler pipeline.

saying these in an interview costs you the question

  • Thinks a missing named import quietly yields undefined
  • Says default exports are a different mechanism from named exports
  • Believes a module can have several default exports
  • Claims named imports can be renamed but default imports cannot
  • Writes import { default } from './m.js' and expects it to parse

context

open as a page

In an ES module, what does `export * from './utils.js'` actually re-export, and what does it leave out?

level: middleimportance: should knowfreq 50%

basics

~10 s

It forwards every named export of ./utils.js under the same names, and deliberately excludes that module's default export. It also creates no local bindings, so the re-exporting file cannot use those names itself.

open as a page

In an ES module barrel file, what is the difference between writing `export { parse } from './parser.js'` and writing `import { parse } from './parser.js'; export { parse };`?

level: middleimportance: should knowfreq 38%

basics

~20 s

Consumers see the same export either way, but the from-form creates no local binding: the barrel itself cannot reference parse. The import-then-export version does bind parse in module scope, so the file can also use the value.

open as a page

When designing an ES module's public surface, what are the practical tradeoffs between exposing values as named exports versus a single default export?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Named exports keep the public name under the author's control, so identifiers stay consistent, greppable and safely renameable by tooling. A default export makes a one-artifact file read cleanly but hands the naming decision to every importer, which invites drift.

open as a page