JavaScript Named Exports: A Complete Tutorial
Named exports let you share multiple values from a JavaScript module by name. Learn the syntax, import patterns, aliasing, and re-exporting with clear examples.
JavaScript named exports let you share multiple values from one module to another by name. Instead of exporting a single default value, you mark individual variables, functions, or classes with the export keyword, and other files import them by name using curly braces.
Here is the smallest useful example. One file exports two functions, and another file imports exactly the ones it needs:
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}The export keyword in front of each function declaration makes it available to other modules. The second file now imports only the functions it needs:
// app.js
import { add, subtract } from "./math.js";
console.log(add(5, 3)); // 8
console.log(subtract(10, 4)); // 6The import uses destructuring-style curly braces to pull in exactly the names you need. You write the names between the braces, and JavaScript matches them to the exported values from the source module.
Export Syntax Patterns
You can apply the export keyword in several ways. Each pattern below is valid, and the choice depends on how you prefer to organize your code.
Inline Export on Declaration
Put export directly before the declaration. This is the most common pattern because the visibility is immediate:
// constants.js
export const API_URL = "https://api.example.com";
export const MAX_RETRIES = 3;
export function fetchUser(id) {
return fetch(`${API_URL}/users/${id}`);
}You know at a glance which values leave the module because the export keyword sits right next to each declaration. This inline style is the most readable pattern for small modules, keeping exports tightly coupled to their definitions.
The same approach works for classes too, which are commonly exported this way in object-oriented code. Here is how a class looks with the export keyword applied directly to its declaration statement:
export class UserService {
constructor(token) {
this.token = token;
}
}Any file can now import and instantiate the class with its constructor argument.
Export List at the Bottom
Declare everything privately, then export only what you want to share at the end of the file. This gives you a clean public API summary in one place:
// utils.js
function formatDate(date) {
return date.toISOString().split("T")[0];
}
function formatCurrency(amount) {
return `$${amount.toFixed(2)}`;
}The internal helper function stays private by never appearing in the export statement at the bottom of the source file:
function internalHelper() {
return "helper";
}
export { formatDate, formatCurrency };The function internalHelper stays private to the module because it is not listed in the export statement. No other file can access it.
Importing Named Exports
When you import named exports, the names must match exactly. The import statement is a static declaration that goes at the top of the file and cannot be wrapped in conditionals:
import { add, subtract } from "./math.js";If you try to import a name that does not exist, the browser throws an error. Here is what you would see:
SyntaxError: The requested module './math.js' does not provide an export named 'multiply'Renaming Imports with the as Keyword
If two modules export the same name, or you want a clearer local name, use the as keyword to rename during import:
import { formatDate as format } from "./date-utils.js";
import { formatDate } from "./logger.js";
// format refers to date-utils version, formatDate refers to logger version
console.log(format(new Date()));This avoids naming collisions without changing either source module.
Import All Named Exports as a Namespace
When you need everything from a module, import it all under a single namespace object. This creates one convenient reference that holds every named export:
import * as MathUtils from "./math.js";
console.log(MathUtils.add(2, 3)); // 5
console.log(MathUtils.subtract(10, 2)); // 8The star import creates an object whose properties are every named export. This pattern is useful when a module has many exports and you want to group them under a clear prefix.
Renaming on Export
You can also rename during export with the as keyword. This is useful when the internal name of a function differs from the public API name you want to expose to consumers of your module.
It also helps when refactoring: change the internal name freely, and keep the exported name stable so existing importers do not break. Here is how it works:
// store.js
function fetchUserData(id) {
return { id, name: "Alice" };
}
export { fetchUserData as getUser };Now consumers import it as getUser, not fetchUserData. The import side does not need to know the original internal name, and you can change fetchUserData later without touching every file that imports the module:
import { getUser } from "./store.js";
console.log(getUser(1)); // { id: 1, name: "Alice" }The import side does not need to know the original internal name.
Re-Exporting from Barrel Files
A barrel file collects exports from several modules and re-exports them from one place. This lets consumers import from a single path instead of hunting through multiple files.
Three utility modules each export their functions. The barrel file joins them together, so app.js only needs one import line. Here are the individual source files, each exporting its own functions for other modules to import:
// auth.js
export function login() { /* ... */ }
export function logout() { /* ... */ }
// validation.js
export function isValidEmail(email) { /* ... */ }
// formatting.js
export function formatDate(date) { /* ... */ }The barrel file re-exports all of them from one central location, collecting every exported function into a single convenient module:
// index.js (barrel file)
export { login, logout } from "./auth.js";
export { isValidEmail } from "./validation.js";
export { formatDate } from "./formatting.js";Now the consumer imports everything from one clean path, without needing to know which individual files contain each exported function:
// app.js
import { login, logout, isValidEmail, formatDate } from "./utils/index.js";The barrel file is the public face of the utils folder. Consumers do not need to know the internal file organization.
You can also re-export everything from another module at once with a wildcard:
export * from "./auth.js";
export * from "./validation.js";This pattern is shorter but hides which names are actually re-exported. Use the explicit form when the public API should be deliberate and easy to audit.
Common Mistake: Confusing Named and Default Import Syntax
Beginners often mix up the import syntax for named and default exports. The critical difference between them is the presence or absence of curly braces around the imported name.
With a default export, you import without braces. With a named export, braces are required. Getting this wrong gives you undefined instead of the function you expected:
// math.js
export function add(a, b) { return a + b; }
// Wrong -- imports the default export, which does not exist here
import add from "./math.js"; // add is undefined
// Correct -- curly braces tell JavaScript to look for a named export
import { add } from "./math.js"; // add is the functionCurly braces around the import name are not optional decoration. They tell JavaScript to look for a specific named export rather than the default one. If you forget them, the import silently resolves to undefined rather than throwing an obvious error, which can be hard to debug.
Common Mistake: Trying to Reassign Imported Values
Imported bindings are read-only by design. You cannot reassign them directly, and attempting to do so throws a runtime error.
This is true for both named and default imports, because the module system enforces immutability on imported values. The restriction exists because modules are meant to be predictable: if one file could change another file's exports, debugging would become chaotic.
import { API_URL } from "./config.js";
API_URL = "https://new-url.com"; // TypeErrorIf you need a local copy you can modify, assign it to a new variable first. The import binding itself stays untouched, and your local variable is free to change as needed. This pattern is common when you need to apply overrides, transformations, or fallback values without affecting the original imported constant:
import { API_URL } from "./config.js";
let url = API_URL;
url = "https://override-url.com"; // This works fineThis local-copy pattern is reliable and keeps the module's contract intact. The original export remains unchanged for every other consumer.
When to Use Named Exports
Use named exports when a module has several related utilities, constants, or functions that consumers may want to pick and choose from. This is the most common pattern for utility modules, helper libraries, and shared constants. Named exports also work better with tree shaking because bundlers can statically analyze which exports are used and remove the rest -- a key advantage for production bundle sizes.
If a module has only one primary value to share, consider a default export instead. For modules that need both, pair a default export with a few named helper exports. This hybrid pattern is common in popular libraries and pairs well with the ES6 module import/export system that all modern JavaScript projects use.
Rune AI
Key Insights
- Named exports let you export multiple values from a single module.
- Import them with destructuring syntax using curly braces and the exact name.
- Use the
askeyword to rename exports or imports for clarity. - Re-export from barrel files to create clean public APIs.
- Named exports work better with tree shaking than default exports.
Frequently Asked Questions
What is the difference between named and default exports?
Can a module have both named and default exports?
Do I need to use curly braces when importing named exports?
Conclusion
Named exports are the standard way to share multiple values from a JavaScript module. They keep imports explicit and let bundlers tree-shake unused code. Use them when a module exposes several related functions, constants, or classes. Pair them with default exports when a module has one main value and several helper utilities.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.