← Back to JavaScript series
🧩
Browser APIs
Advanced Β· Prerequisite: 22_Storage

23. Modules (ESM)

Putting everything in one file leads to naming collisions and pain. Modules isolate scope per file and only expose what you choose.

ES Modulesimportexportreuse
Duration
⏱ ~1 hour
Level
πŸ“Š Intermediate
Prerequisite
🎯 Lecture 22
OUTCOME
Putting everything in one file leads to naming collisions and pain. Modules isolate scope per file and only expose what you choose.

What you'll learn

  • 1Use `import`/`export` from ES Modules.
  • 2Tell named and default exports apart.
  • 3Load modules in the browser with `<script type="module">`.
  • 4Bundle modules together with re-exports.

Overview

Putting everything in one file leads to naming collisions and pain. Modules isolate scope per file and only expose what you choose.

Core Concepts

1. Why modules?

Putting everything in one file leads to naming collisions and pain. Modules isolate scope per file and only expose what you choose.

2. export β€” named / default

javascript
// math.mjs
export const PI = 3.14159;
export function add(a, b) { return a + b; }

export default function multiply(a, b) { return a * b; }

Named exports keep their names; a module can have at most one default export.

3. import

javascript
import multiply, { PI, add } from './math.mjs';
import * as math from './math.mjs';
import { add as plus } from './math.mjs';

Default goes without braces; named imports go inside braces.

4. In the browser

html
<script type="module" src="./02_import.mjs"></script>

`type="module"` is required for `import` to work. Module scripts are auto-deferred and run in strict mode.

5. Re-export

For bundling a single entry point that re-exports multiple modules.

javascript
// index.mjs
export { add, PI } from './math.mjs';
export { format } from './format.mjs';

Examples

FileWhat it covers
01_export.mjsNamed & default exports
02_import.mjsImport from 01
03_named_default.mjsMixing both
04_reexport.mjsRe-export pattern
index.html`<script type="module">`

Open `index.html` via a local server (`file://` will fail due to CORS).

src/01_export.mjs

mjs
/**
 * Named export + default export example
 */
export const PI = 3.14159;

/**
 * Sum of two numbers
 */
export function add(a, b) {
  return a + b;
}

/**
 * Product of two numbers (default)
 */
export default function multiply(a, b) {
  return a * b;
}

src/02_import.mjs

mjs
/**
 * Import from 01_export.mjs
 */
import multiply, { PI, add } from './01_export.mjs';

console.log('PI:', PI);
console.log('1+2 =', add(1, 2));
console.log('3*4 =', multiply(3, 4));

src/03_named_default.mjs

mjs
/**
 * Combine named/default imports and use aliases
 */
import multiply, { add as plus, PI } from './01_export.mjs';
import * as math from './01_export.mjs';

console.log(plus(10, 20));
console.log(multiply(2, PI));
console.log('namespace:', math.add(5, 7));

src/04_reexport.mjs

mjs
/**
 * Re-export β€” bundle multiple modules into one entry point.
 * Callers only import this file.
 */
export { add, PI } from './01_export.mjs';
export { default as multiply } from './01_export.mjs';

src/index.html

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>ESM demo</title>
  </head>
  <body>
    <h1>ESM demo</h1>
    <p>Check the console.</p>
    <script type="module" src="./02_import.mjs"></script>
  </body>
</html>

Common Mistakes

  1. Forgetting `type="module"` and getting a SyntaxError on `import`.
  2. Wrapping a default import in `{ ... }`.
  3. Omitting `./` from a relative path so it looks like a bare module.
  4. Dropping the `.mjs`/`.js` extension (the browser needs it).
  5. Opening with `file://` directly β€” use a local server.

FAQ

Q1. `.js` vs `.mjs`?

In Node, `.mjs` is always ESM; `.js` depends on the package.json `type` field. In the browser both work as long as `type="module"` is set. **Q2. Can I mix CommonJS and ESM?** A. There's limited support in Node β€” covered in Lecture 24. **Q3. Is there dynamic import?** A. Yes β€” `await import('./x.mjs')` loads at runtime.

Practice

Build a tiny utilities module and use it from an HTML page.

homework/README.md

## Homework 1 β€” In `utils.mjs`, export two functions as named exports: `formatPrice(n)` (e.g. 1000 β†’ "1,000 KRW") and `discount(price, percent)`. Import them in `main.mjs` and render to an HTML page.

homework/answer/homework_01.html

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Utils module demo</title>
  </head>
  <body>
    <h1>Price demo</h1>
    <div id="out"></div>
    <script type="module" src="./homework_01_main.mjs"></script>
  </body>
</html>

homework/answer/homework_01_main.mjs

mjs
/**
 * Use the utils module and render to the page
 */
import { formatPrice, discount } from './homework_01_utils.mjs';

const price = 25000;
const sale = discount(price, 20);

const out = document.getElementById('out');
out.innerHTML = `
  <p>Regular: ${formatPrice(price)}</p>
  <p>Sale (20%): ${formatPrice(sale)}</p>
`;

homework/answer/homework_01_utils.mjs

mjs
/**
 * Format a price with thousands separators and "KRW"
 */
export function formatPrice(n) {
  return `${n.toLocaleString('ko-KR')} KRW`;
}

/**
 * Apply a percentage discount
 */
export function discount(price, percent) {
  return Math.floor(price * (1 - percent / 100));
}

Next Lecture

[24_Node_Environment](../../07_Node_μž…λ¬Έ/24_Node_ν™˜κ²½/)

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—