JavaScript Named Exports a Complete Tutorial
A complete tutorial on JavaScript named exports. Covers inline and bottom-of-file export syntax, import with renaming, namespace imports, re-exporting, tree-shaking benefits, barrel files, and why many teams prefer named exports over defaults for large codebases.
Named exports let you expose multiple bindings from a JavaScript module, each identified by a specific name. Importers must use that exact name (or explicitly alias it), which enforces consistency across a codebase and enables bundlers to tree-shake unused exports.
Inline Named Exports
Place export before a declaration to make it a named export:
// math.js
export const PI = 3.14159;
export const E = 2.71828;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
magnitude() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
}
}Every export-prefixed declaration becomes part of the module's public API.
List Export (Bottom-of-File)
Declare everything first, then export selected bindings in one statement:
// string-utils.js
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function slugify(str) {
return str.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
}
function truncate(str, maxLen = 100) {
return str.length > maxLen ? str.slice(0, maxLen) + "..." : str;
}
// Internal helper — not exported
function normalizeWhitespace(str) {
return str.replace(/\s+/g, " ").trim();
}
export { capitalize, slugify, truncate };This style makes the public API visible in one place and keeps private helpers unexported.
Importing Named Exports
import { add, subtract, PI } from "./math.js";
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159Import With Alias
import { add as sum, subtract as minus } from "./math.js";
console.log(sum(2, 3)); // 5
console.log(minus(5, 2)); // 3Aliasing is useful to avoid name collisions when importing from multiple modules. For the destructuring equivalent of renaming, see renaming variables during JS destructuring guide.
Namespace Import
import * as math from "./math.js";
console.log(math.PI); // 3.14159
console.log(math.add(2, 3)); // 5
console.log(math.Vector); // [class Vector]The namespace object contains all named exports (and .default if a default exists).
Renaming on Export
// internal.js
function fetchUserData(id) { /* ... */ }
function fetchOrderData(id) { /* ... */ }
export {
fetchUserData as getUser,
fetchOrderData as getOrder,
};Consumers see getUser and getOrder. The internal names remain private to the module.
Re-Exporting Named Exports
Barrel files aggregate exports from multiple modules:
// services/index.js
export { getUser, updateUser } from "./userService.js";
export { getOrder, createOrder } from "./orderService.js";
export { sendEmail } from "./emailService.js";Re-Export Everything
// utils/index.js
export * from "./string-utils.js";
export * from "./date-utils.js";
export * from "./number-utils.js";If two modules export the same name, export * causes an ambiguity error. Use explicit re-exports to resolve conflicts.
Tree-Shaking and Named Exports
Bundlers (webpack, Rollup, Vite) perform tree-shaking by removing unused exports from the final bundle:
// All exported individually
export function a() { /* ... */ }
export function b() { /* ... */ }
export function c() { /* ... */ }// Only 'a' is imported — b and c are tree-shaken out
import { a } from "./utils.js";| Export Style | Tree-Shakable? | Why |
|---|---|---|
| Named exports | Yes | Each export is independently trackable |
| Default export (function/class) | Partially | Depends on bundler analysis |
| Default export (object) | No | Bundler cannot determine which properties are used |
This is the primary technical reason to prefer named exports in libraries.
Named vs Default: When to Choose
| Criteria | Named Export | Default Export |
|---|---|---|
| Import syntax | import { X } from | import X from |
| Multiple per module | Unlimited | One per module |
| Name consistency | Enforced (must match or alias) | Consumer chooses any name |
| IDE autocomplete | Excellent | Limited |
| Refactoring support | Strong | Weak |
| Tree-shaking | Full support | Limited for objects |
| Best for | Utility modules, libraries | Single-class modules, configs |
See JavaScript default exports complete tutorial for the default export perspective.
Practical Patterns
Feature Module Barrel
// features/auth/index.js
export { AuthProvider } from "./AuthProvider.js";
export { useAuth } from "./useAuth.js";
export { LoginForm } from "./LoginForm.js";
export { SignupForm } from "./SignupForm.js";
export { AUTH_ROUTES } from "./routes.js";// App.js — clean import path
import { AuthProvider, useAuth, LoginForm } from "./features/auth/index.js";Constants Module
// constants/http.js
export const STATUS_OK = 200;
export const STATUS_CREATED = 201;
export const STATUS_BAD_REQUEST = 400;
export const STATUS_NOT_FOUND = 404;
export const STATUS_SERVER_ERROR = 500;
export const METHODS = Object.freeze({
GET: "GET",
POST: "POST",
PUT: "PUT",
DELETE: "DELETE",
PATCH: "PATCH",
});Export Type Alongside Runtime Value
// In TypeScript, named exports work for both types and values
export interface User {
id: string;
name: string;
email: string;
}
export function createUser(data: Partial<User>): User {
return { id: crypto.randomUUID(), name: "", email: "", ...data };
}Common Mistakes
Forgetting Curly Braces
// Wrong — imports the default (which may not exist)
import add from "./math.js";
// Correct — imports the named export 'add'
import { add } from "./math.js";Exporting and Immediately Destructuring
// This does NOT create named exports
const utils = { capitalize, slugify };
export default utils;
// Consumer can't tree-shake individual functions:
import utils from "./utils.js";
utils.capitalize("hello"); // works, but not tree-shakable
// Better: export individually
export { capitalize, slugify };Rune AI
Key Insights
- Each named export is independently importable: Consumers pick exactly what they need, nothing more
- Tree-shaking works best with named exports: Unused named exports are removed from the bundle; default-exported objects are not shakable
- Curly braces distinguish named from default imports:
{ X }is named,Xis default -- mixing them up is a top source of import bugs - Barrel files centralize module APIs: Re-export from an index file to keep import paths clean without losing tree-shaking
- Named exports produce live bindings: Changes to exported
letvariables are reflected in all importing modules in real time
Frequently Asked Questions
Can I mix named and default exports in one module?
Do named exports work with dynamic import()?
Can I export the same binding under multiple names?
Are named export bindings live?
Does the order of exports matter?
Conclusion
Named exports are the foundation of modular JavaScript. They enforce naming consistency, enable full tree-shaking, and work seamlessly with IDE tooling. For utility modules, constant files, and any module exporting multiple values, named exports are the standard recommendation. For the overall module system and re-export patterns, see JavaScript ES6 modules import export guide. For spread-based data manipulation within modules, see JS spread vs rest operator complete tutorial.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.