JavaScript Modules
Organize JavaScript code with ES6 modules using named exports, default exports, and dynamic imports.
TL;DR
- 01Export functions and variables using named or default export syntax.
- 02Import named exports using curly braces and defaults without them.
- 03Use dynamic
import()to load modules asynchronously at runtime.
Tips
- 01Use named exports for multiple utilities and reserve default exports for the primary object a module provides.
- 02Create barrel files named
index.jsto simplify deeply nested import statements across your application subfolders.
Warnings
- 01Avoid circular module dependencies where two files import each other, as this can generate unexpected
undefinedbindings. - 02Attempting to declare more than one default export in a single module throws a compile-time
SyntaxError.
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();
}In Practice
Loads visual style themes dynamically at runtime using asynchronous imports to minimize the initial application bundle size.
- 01Construct the dynamic path to the theme file based on user selection.
- 02Invoke the dynamic
import()function to request the module asynchronously. - 03Access the default theme export from the resolved module namespace.
- 04Call the apply method on the theme object to update user styles.
- 05Catch 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.
Circular dependencies cause modules to evaluate before their dependencies finish exporting. This leaves unfinished variables as undefined. Resolve this cycle by extracting shared variables to a third module.