JavaScript Modules
Organize JavaScript code with ES6 modules using named exports, default exports, and dynamic imports.
TL;DR
- Export functions and variables using named or default export syntax.
- Import named exports using curly braces and defaults without them.
- Use dynamic
import()to load modules asynchronously at runtime.
Named Exports
exportExposes functions, variables, or classes from a module under specific names.
// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14159;import { ... }Imports specific named exports from another module file using curly brace syntax.
import { add, PI } from "./math.js";
console.log(add(5, PI));import * as namespaceBinds all named exports to a single namespace object variable.
import * as Math from "./math.js";
console.log(Math.add(5, 3));
console.log(Math.PI);Barrel re-exportRe-exports items from other modules directly without importing them locally first.
// index.js - barrel file
export { add, subtract } from "./math.js";
export { formatDate } from "./date.js";Default Exports
export defaultExposes a single primary export value, function, or class from a module.
// logger.js
export default function log(msg) {
console.log(`[LOG] ${msg}`);
}Default importImports a default export without using curly braces, using any local name.
import log from "./logger.js";
log("App started");Class exportExports an entire ES6 class definition as the default export of a module.
// UserService.js
export default class UserService {
getUser(id) { return { id }; }
}Mixing and Re-exporting
Mixed importsImports both default and named exports within a single import statement.
import main, { helper, VERSION } from "./utils.js";
main();Default re-exportRe-exports a default export as a named export inside barrel files.
export { default as User } from "./User.js";Renamed re-exportRenames exports during the re-export process for public API clarity.
export { add as sum } from "./math.js";Renaming Imports
import asRenames imported values to prevent naming collisions with other local variables.
import { add as addition } from "./math.js";
addition(5, 3);Conflict resolutionAllows importing functions with identical names by mapping them to local aliases.
import { format as formatDate } from "./date.js";
import { format as formatMoney } from "./currency.js";Module Side Effects
Side-effect importImports a module purely for its side effects without binding any local variables.
import "./polyfills.js";
import "./analytics.js";Cached evaluationEvaluates modules only once per application lifecycle, caching subsequent imports.
import "./init.js"; // executes
import "./init.js"; // loads from cachedynamic import()Loads modules dynamically and asynchronously at runtime using promise logic.
async function loadChart() {
const { Chart } = await import("./chart.js");
return new Chart();
}Tips
- Use named exports for multiple utilities and reserve default exports for the primary object a module provides.
- Create barrel files named
index.jsto simplify deeply nested import statements across your application subfolders.
Warnings
- Avoid circular module dependencies where two files import each other, as this can generate unexpected
undefinedbindings. - Attempting to declare more than one default export in a single module throws a compile-time
SyntaxError.
In Practice
Loads visual style themes dynamically at runtime using asynchronous imports to minimize the initial application bundle size.
- Construct the dynamic path to the theme file based on user selection.
- Invoke the dynamic
import()function to request the module asynchronously. - Access the default theme export from the resolved module namespace.
- Call the apply method on the theme object to update user styles.
- Catch and report loading errors if the selected theme is not found.
export async function loadTheme(name) {
try {
const path = `./themes/${name}.js`;
const module = await import(path);
const theme = module.default;
theme.apply();
} catch (err) {
console.error(`Load failed: ${name}`, err);
}
}FAQ
Use a default export for the primary component or class that a module provides. Choose named exports for utility functions or constants. This keeps imports consistent and easy to read.
Combine both exports into a single statement: import MyClass, { helperFn, CONST } from './module.js'. Place the default export before the curly braces containing the named exports.
Use the as keyword: import { render as renderList } from './list.js'. To rename default imports, simply choose a fresh local variable identifier during the import declaration.
Dynamic import() loads modules on demand at runtime and returns a Promise resolving to the module. Use it for lazy loading, route code splitting, or loading conditional scripts.