How do you share code between files?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
Through modules. A file exports what it wants to make available, and other files import it. Everything not exported stays private to that file.
// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14159;
const secret = 42; // not exported — invisible elsewhere
// app.js
import { add, PI } from "./math.js";
Named and default exports
export default function Button() {} // one per file
import Button from "./Button.js"; // name it whatever you like
export { add, subtract }; // any number
import { add, subtract } from "./math.js";
import * as math from "./math.js"; // everything under one namespace
Named exports are the better default: the name is fixed, so search and rename work reliably across the codebase, and tree shaking can drop what you do not import. Default exports let each importer invent its own name, which makes a symbol harder to trace.
Renaming and re-exporting
import { add as sum } from "./math.js";
export { add } from "./math.js"; // pass through without importing
A "barrel" file re-exporting a whole folder is convenient but can pull in far more code than intended — use it sparingly.
Loading on demand
const { heavy } = await import("./heavy.js");
Dynamic import() returns a promise and creates a separate chunk, which is how you defer code behind an interaction.
A note on CommonJS
Older Node code uses require() and module.exports. It still works, but write ES modules for anything new — they are the standard, they support tree shaking, and mixing the two causes friction in one direction: ESM can import CommonJS, but CommonJS cannot require() an ESM-only package.
Two practical points: imports are hoisted and run before the rest of the file, and circular imports resolve to a partially initialised module rather than failing loudly — if a value is unexpectedly undefined at import time, suspect a cycle.
