Skip to content
TypeScript ts modules 3 min read

Default Exports

A module can designate exactly one default export — the value a consumer gets when they import without braces. Defaults are common in React component files and many npm packages, where the module has one obvious “main thing.” But unlike named exports, a default export has no canonical name, which weakens autocomplete and refactoring. This page shows how to declare and consume default exports, how they interoperate with named exports, and why most modern TypeScript style guides lean toward named exports.

Declaring a default export

Use export default before a value, function, or class. There can be only one per module.

// logger.ts
export default class Logger {
  log(message: string): void {
    console.log(`[LOG] ${message}`);
  }
}

You can also default-export an expression, a function, or an already-declared name:

// add.ts
export default function add(a: number, b: number): number {
  return a + b;
}

// config.ts
const config = { retries: 3, timeout: 5_000 };
export default config;

You cannot put export default in front of a let/const on the same line for multiple values, and you cannot default-export an interface and use it as a value — defaults export one binding only.

Importing a default export

Import a default with no braces, and you choose the local name. Nothing forces it to match the original.

import Logger from "./logger.js";
import sum from "./add.js";       // could be named anything
import settings from "./config.js";

const l = new Logger();
l.log("ready");
console.log(sum(2, 3), settings.retries);

Because the import name is arbitrary, two files can name the same default differently — which is exactly why defaults are harder to track across a codebase.

Mixing default and named exports

A module can have one default export plus any number of named exports. This is the pattern many libraries use.

// api.ts
export default function request(url: string): Promise<Response> {
  return fetch(url);
}

export const BASE_URL = "https://api.example.com";
export interface RequestOptions {
  method?: "GET" | "POST";
}

Consumers combine both forms in a single import statement, with the default first:

import request, { BASE_URL, type RequestOptions } from "./api.js";

Why named exports are often preferred

Default exports trade discoverability and refactor-safety for a slightly shorter import. The table summarizes the trade-offs that lead many teams to standardize on named exports.

ConcernNamed exportDefault export
Import nameFixed; must match (or alias with as)Arbitrary; chosen at each site
Editor autocompleteSuggested by name on importNot suggested — you type the name
Rename refactorUpdates all referencesReferences must be fixed by hand
Re-export in barrelsexport { x } fromNeeds export { default as X }
Tree-shakingExcellentExcellent

Because a default export has no intrinsic name, “find all references” and automated renames are less reliable, and barrels must rename the default explicitly:

// index.ts
export { default as Logger } from "./logger.js";

When defaults still make sense

Defaults are reasonable when a module genuinely represents a single thing and an ecosystem convention expects it — for example, a React component per file, or following a framework’s file conventions.

// Button.tsx
export default function Button({ label }: { label: string }) {
  return <button>{label}</button>;
}

Even then, many style guides (and ESLint’s import/no-default-export) prefer named exports for consistency, so the choice is largely a team convention.

verbatimModuleSyntax and CommonJS interop edge cases can make export default behave differently from export =. If you target CommonJS, prefer named exports or test the emit. See Type-Only Imports & Exports.

Best Practices

  • Default to named exports for application code — they are explicit and refactor-safe.
  • Allow at most one default export per module, and only when the module has one clear purpose.
  • Follow ecosystem conventions (e.g. one React component per file) where defaults are expected.
  • Re-export defaults as named symbols in barrels: export { default as Name } from.
  • Avoid renaming a default differently across files; pick one name and stick to it.
  • Consider an ESLint rule (import/no-default-export) to enforce a consistent policy.
Last updated June 29, 2026
Was this helpful?