The Codex / JavaScript / Modules (ESM)

Modules (ESM)

Organising JavaScript into reusable files — import, export, named vs default, and ESM vs CommonJS.

What it is, in plain English: A module is a JavaScript file that explicitly exports what it wants to share and imports what it needs from other files. Modules solve the problem of large programs becoming one tangled mess — they let you split code into focused, reusable pieces. ES Modules (ESM) is the standard: use 'export' to share, 'import' to use. Node.js also supports CommonJS (require/module.exports) — understanding both is essential for working in the JS ecosystem.

Splitting code into reusable files

As programs grow, putting everything in one file becomes unmanageable. Modules let you split code into focused files — one file for maths utilities, one for user-related functions, one for API calls — and connect them with import and export.

Exporting from a module

// math.js — a module that exports useful maths functions

// Named exports — you can have many:
export function add(a, b)      { return a + b; }
export function subtract(a, b) { return a - b; }
export function multiply(a, b) { return a * b; }
export const PI = 3.14159265;

// Or export at the bottom (equivalent):
function divide(a, b) {
  if (b === 0) throw new Error("Cannot divide by zero");
  return a / b;
}
export { divide };
// user.js — a module with a default export

// Default export — only ONE per module:
export default class User {
  constructor(name, email) {
    this.name  = name;
    this.email = email;
  }
  greet() { return `Hello, I'm ${this.name}`; }
}

// You can mix default and named exports:
export const ROLES = ["admin", "user", "viewer"];

Importing from a module

// app.js — using the modules above

// Named imports — curly braces, exact names:
import { add, multiply, PI } from "./math.js";

console.log(add(2, 3));           // 5
console.log(multiply(PI, 2));     // 6.28...

// Default import — no curly braces, any name you choose:
import User from "./user.js";

const alice = new User("Alice", "alice@example.com");
console.log(alice.greet());       // "Hello, I'm Alice"

// Import both default and named:
import User, { ROLES } from "./user.js";
console.log(ROLES);               // ["admin", "user", "viewer"]

// Import everything as a namespace object:
import * as Math from "./math.js";
console.log(Math.add(2, 3));      // 5
console.log(Math.PI);             // 3.14159265

// Rename on import (avoid naming conflicts):
import { add as mathAdd } from "./math.js";
import { add as vectorAdd } from "./vectors.js";

Using modules in the browser

<!-- Use type="module" on your script tag -->
<script type="module" src="app.js"></script>

<!-- Inline module -->
<script type="module">
  import { add } from "./math.js";
  console.log(add(1, 2));
</script>

Important: modules are always in strict mode automatically. They have their own scope — variables don't leak to the global window. They're deferred by default (like defer attribute).

Using modules in Node.js

// Option 1: use .mjs extension
// math.mjs — same export syntax as above

// Option 2: add "type": "module" to package.json
// {
//   "type": "module"
// }
// Then .js files use ESM syntax

// With "type": "module" in package.json:
// math.js uses: export function add...
// app.js uses:  import { add } from "./math.js"
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.