JavaScript module: A practical guide
A practical, in depth guide to JavaScript modules, covering ES modules and CommonJS, browser and Node.js usage, dynamic imports, barrels, tree shaking, and patterns for scalable code.

javascript module is a unit of reusable code that encapsulates functionality and exposes a public API via exports while keeping internal details private; in modern JavaScript it refers to ES modules that use import and export statements.
What is a JavaScript module
According to JavaScripting, a JavaScript module is a self contained unit of code that exports functionality and keeps internal details private. In practice, modules live in separate files and expose a public API through export statements. Consumers import that API with import statements, enabling a clean boundary between concerns and reusable code across projects.
A modern module uses ES Module syntax, which standardizes import and export. With ES modules, you can export functions, objects, classes, or primitive values. Importers bring those exports into their own scope, and because modules are cached, a module is executed only once per page load or per Node process. This simple contract—export what you want to share, import what you need—dramatically improves maintainability.
Consider a tiny example: a module named math.js that exposes add and pi, and a consumer file app.js that uses them. The example below demonstrates named exports and a straightforward import.
// math.js
export function add(a, b) { return a + b; }
export const pi = 3.14159;
// app.js
import { add, pi } from './math.js';
console.log(add(2, 3), pi);``
The evolution of module systems in JavaScript
JavaScript historically operated with global scope, which made code hard to maintain as apps grew. Node.js popularized CommonJS, using require and module.exports for server side code. As browsers matured, the community converged on ES modules, introducing a standard import/export syntax that supports static analysis, improved tooling, and better tree shaking. JavaScripting analysis shows that teams benefit from clear module boundaries, predictable loading, and fewer global variables when they adopt ES modules early in a project. This history matters because it guides tooling choices, such as when to rely on bundlers, loaders, or native browser support. When you know your runtime environment, you can decide whether to use type definitions, file extensions, and package.json settings that align with ES modules or CommonJS. In practice, most modern projects aim for ES modules in browser code and either default to ES modules in Node or use transpilation to bridge gaps. The key takeaway is to align the module system with your deployment target to avoid friction later.
Import and export syntax in depth
The core of any JavaScript module system is how you export and import functionality. Named exports allow you to expose multiple bindings, while a default export provides a single primary value. For example, a module could export add and pi as named exports, so consumers import only what they need:
// modules/math.js
export function add(a, b) { return a + b; }
export const pi = 3.14159;// app.js
import { add, pi } from './modules/math.js';
console.log(add(2, 3), pi);A default export enables a single main value per file:
// modules/greeter.js
export default function greet(name) { return `Hello ${name}`; }// app.js
import greet from './modules/greeter.js';
console.log(greet('Ada'));Dynamic imports with import() return a Promise and let you load code on demand for performance gains and code splitting:
import('./modules/math.js').then(m => {
console.log(m.add(1, 2));
});Master the nuances of import paths, relative vs absolute URLs, and how bundlers may rewrite modules for production.
Questions & Answers
What is the difference between ES modules and CommonJS?
ES modules use static import and export syntax that allows for static analysis and better tooling. CommonJS uses require and module.exports and is primarily used in Node.js traditional environments. ES modules are supported in browsers and modern Node.js, while CommonJS remains common in legacy server code.
ES modules use static import and export, while CommonJS relies on require and module.exports. ES modules work in browsers and modern Node.js, making them the more future oriented choice.
How do I import a default export vs named exports?
Named exports import specific bindings by name using curly braces. A default export imports a single primary value without curly braces. Example: import { add } from './mod.js' versus import add from './mod.js'.
Use named exports with curly braces for multiple bindings, or default export for a single primary value.
Can I use modules in the browser without a bundler?
Yes. Modern browsers support ES modules via script tags with type="module". Bundlers are still useful for optimizing and bundling, but you can run modular code directly in many environments today.
Yes, browsers can load ES modules directly with type module, though bundlers help with optimization.
What is a barrel file?
A barrel file is an index.js that re-exports selected modules, providing a single entry point for consumers and hiding internal file paths. It simplifies imports and improves API surface control.
A barrel file re-exports modules to create a clean, single entry point for consumers.
How does dynamic import work and when should I use it?
Dynamic import loads modules on demand by returning a Promise. It is ideal for code splitting, feature flags, or loading heavy dependencies only when needed.
Use dynamic import to load modules on demand, improving initial load times.
Do modules affect performance?
Modules enable tree shaking and code splitting, which can reduce bundle sizes. They can also introduce asynchronous loading; balance startup cost against feature needs and use tooling to optimize.
Modules can improve performance through tree shaking and splitting, but plan loading strategy carefully.
What to Remember
- Export only what you share to enable tree shaking
- Prefer named exports for clarity and safer refactors
- Use ES modules consistently across environments
- Understand the difference between ES modules and CommonJS
- Use dynamic imports for code splitting and on demand loading