Modules communicate through two mechanisms: export makes a value available, and import brings it in.
Named exports
Named exports expose values by their declared name. A module can have any number of named exports:
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
Importing named exports
Import uses the same names in curly braces:
// app.js
import { add, subtract } from "./math.js";
add(2, 3); // 5
subtract(10, 4); // 6
The names in the import must match the exported names:
import { add, PI } from "./math.js"; // importing specific exports
Renaming during import
Rename imports to avoid conflicts:
import { add as sum, PI as TAU } from "./math.js";
// sum and TAU are now the local names
Default exports
A module can have one default export — a primary value that the module is “about”:
// logger.js
export default function log(message) {
console.log(`[LOG] ${message}`);
}
Importing default exports
Default imports do not use curly braces. You choose the name:
import log from "./logger.js";
log("Hello"); // [LOG] Hello
The name log is arbitrary — it is whatever you write after import. The default export is whatever was declared after export default.
Named and default exports together
A module can combine both:
// utils.js
export function formatDate(date) {
return date.toISOString().split("T")[0];
}
export default function initialize() {
console.log("App initialized");
}
import initialize, { formatDate } from "./utils.js";
// default import first, then named imports in braces
Re-exporting
You can re-export from another module without importing it locally:
// index.js — barrel file
export { add, subtract } from "./math.js";
export { formatDate } from "./dates.js";
export { default as Logger } from "./logger.js";
This pattern is common in libraries that want to provide a single entry point for many modules.
Importing entire modules
Import everything from a module as a namespace object:
import * as math from "./math.js";
math.add(1, 2); // 3
math.subtract(5, 3); // 2
math.PI; // 3.14159
This is useful when you need many exports from a module and do not want to list each one.
Side-effect imports
Import a module for its side effects without importing any values:
import "./setup.js"; // runs setup.js but imports nothing
This is used when a module registers something globally — polyfills, global configurations, or initialization code.
File paths
Relative imports use ./ and ../ paths:
import { helper } from "./helper.js"; // same directory
import { api } from "../api.js"; // parent directory
import { config } from "../config/index.js"; // specific file in subdirectory
What can be exported
Any top-level declaration can be exported:
export const VERSION = "1.0";
export let counter = 0;
export function init() { ... }
export class App { ... }
You cannot export things that are not declared:
export 42; // SyntaxError — not a declaration
export default 42; // works — default export accepts any value
Live bindings
Imports are live bindings, not copies. If the exported value changes, the import sees the new value:
// counter.js
export let count = 0;
export function increment() {
count++;
}
// app.js
import { count, increment } from "./counter.js";
console.log(count); // 0
increment();
console.log(count); // 1 — the binding updated
This is different from importing a primitive value in most other languages. The import is a reference to the exported variable, not a snapshot.
What to carry forward
- named exports use
exportbefore a declaration andimport { name }to bring it in - a module can have many named exports but only one default export
- default imports use any name:
import anything from "./module.js" import * as ns from "./module.js"imports everything as a namespace object- re-exporting forwards exports through intermediate modules
- side-effect imports (
import "./setup.js") run a module without importing values - imports are live bindings — changes to the exported variable are visible to importers
- include file extensions in browser imports
Export and import are the mechanism that lets modules communicate. The next lesson covers how to organize files and structure projects around modules.