What Is a Module?

A module is a file that encapsulates part of your code and exposes only what other files need.

Modules make code:

  • easier to organize
  • easier to maintain
  • easier to test
  • easier to reuse

In modern JavaScript, modules are a core part of application structure.

Basic Usage

Enable modules in HTML

<script type="module" src="./main.js"></script>

With type="module":

  • the file is treated as an ES module
  • imports and exports are enabled
  • module code runs in module scope

Export

Named exports

Method 1: inline export

export const PI = 3.14;
export function add(a, b) {
return a + b;
}

Method 2: grouped export

const PI = 3.14;
function add(a, b) {
return a + b;
}
export { PI, add };

Renamed export

const version = "1.0.0";
export { version as appVersion };

Default export

export default function createApp() {
return "app";
}

Each module can only have one default export.

Mixed export

You can use both named and default exports in the same file:

export const version = "1.0.0";
export default function createApp() {
return "app";
}

Import

Import named exports

import { PI, add } from "./math.js";

Import default export

import createApp from "./app.js";

Renamed import

import { appVersion as version } from "./config.js";

Import all named exports

import * as math from "./math.js";
console.log(math.PI);

Mixed import

import createApp, { version } from "./app.js";

Import for side effects only

import "./setup.js";

This is useful when a module should run code on load.

Practical Scenarios

1. Utility module

export function formatDate(date) {
return date.toISOString();
}
export function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}

2. Component module

export function Button() {
return "<button>Click</button>";
}

3. Service module

export async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}

4. Main application module

import { fetchUser } from "./services/user.js";
async function main() {
const user = await fetchUser(1);
console.log(user);
}
main();

Dynamic Import

Dynamic import loads modules on demand.

const module = await import("./heavy-module.js");
module.run();

This is useful for:

  • code splitting
  • lazy loading
  • optional features

Important Notes and Best Practices

1. HTTP(S) requirement in browsers

ES modules in browsers usually need to be served over HTTP(S), not opened directly as local files.

2. Relative and absolute paths

Module paths must be correct and explicit.

3. Module scope

Variables inside a module do not automatically become globals.

4. Module caching

A module is evaluated once and then cached.

5. Circular dependencies

Circular imports are possible, but they make code harder to reason about and should be minimized.

6. Naming conventions

Use clear file names and export names.

7. Performance

Use dynamic import when a module is large or not always needed.

Modules in Modern Tooling

Webpack / Vite

Modern tools treat ES modules as the standard model and build on top of them for bundling, optimization, and code splitting.

Node.js

Node.js supports ES modules as well, though CommonJS still exists in many codebases.

Module Pattern Comparison

ES Modules vs CommonJS

ES Modules:

  • use import and export
  • are the browser standard
  • support static analysis better

CommonJS:

  • uses require and module.exports
  • is older and common in Node.js legacy code

Summary

Modules are the foundation of maintainable modern JavaScript.

Best practices:

  • split code by responsibility
  • keep exports clear
  • avoid unnecessary circular dependencies
  • use dynamic import when lazy loading makes sense