← All material

Core JavaScript · 9 of 9

Exports and Barrel Patterns

ES modules give you three ways to expose things from a file.

Named exports

// format.js
export const formatDate = (d) => { /* ... */ };
export const formatCurrency = (n) => { /* ... */ };

// usage
import { formatDate, formatCurrency } from "./format.js";

Named exports mirror namespace members most closely, and they come with a real benefit: an editor can rename formatDate everywhere it's used. A default export can't offer that — every importer is free to call it whatever they like.

Default exports

// logger.js
export default function (message) { /* ... */ };


// usage
import log from "./logger.js";
import whatever from "./logger.js"; // with default exports you can call the function anything you like

Only use default exports for a file with one obvious main export (a single component / single function). For anything else, use named exports

Re-exports (barrel files) - index.js

// index.js — the folder's public surface
export { formatDate, formatCurrency } from "./format.js";
export { default as log } from "./logger.js";

// ./format-internals.js is absent, so it isn't part of the API

A barrel lists what a folder is willing to hand out. Anything left off it stays an implementation detail you can move or rewrite without checking who imported it.

Prefer export { … } from over export *. The wildcard re-exports whatever the source file happens to expose, so new exports join the public surface the moment someone adds them.

The barrel states the boundary; it doesn't enforce it — a consumer can still reach past it to ./format-internals.js. That needs tooling: an ESLint no-restricted-imports rule, or the exports field in package.json for a published package.