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.
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
// 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
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
<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.
// index.mjs
export { add, PI } from './math.mjs';
export { format } from './format.mjs';Examples
| File | What it covers |
|---|---|
| 01_export.mjs | Named & default exports |
| 02_import.mjs | Import from 01 |
| 03_named_default.mjs | Mixing both |
| 04_reexport.mjs | Re-export pattern |
| index.html | `<script type="module">` |
Open `index.html` via a local server (`file://` will fail due to CORS).
src/01_export.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
/**
* 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
/**
* 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
/**
* 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
<!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
- Forgetting `type="module"` and getting a SyntaxError on `import`.
- Wrapping a default import in `{ ... }`.
- Omitting `./` from a relative path so it looks like a bare module.
- Dropping the `.mjs`/`.js` extension (the browser needs it).
- 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
<!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
/**
* 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
/**
* 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_νκ²½/)
All lecture materials and example code are openly available on GitHub.
View on GitHub β