SECTION 1
Historical Context & Motivation
In the early days of the web, JavaScript programs were tiny — maybe a dozen lines to validate a form or animate a button. Developers dropped all their code into a single <script> tag and called it a day. But as websites grew into full applications like Gmail, Google Maps, and social-media platforms, those single files ballooned into thousands of lines. Finding a bug in a 5,000-line file felt like searching for a specific grain of sand on a beach. The programming community needed a way to break code into smaller, self-contained pieces — and that need gave rise to modules.
1995 • JavaScript Is Born
2009 • CommonJS & Node.js
require() and module.exports, allowing developers to split server-side code into separate files.2015 • ES6 Modules (ESM)
import and export keywords, finally giving JavaScript a built-in, standardized module system for both browsers and servers.2018–Present • Universal Browser Support
<script type="module">. Tools like Webpack, Vite, and ESBuild make module-based development the standard for modern web apps.The central question was straightforward: how can you write code in many small files, each focused on one job, yet combine them seamlessly into a working application? JavaScript's module system is the answer, and understanding it is a critical skill for every modern developer.
SECTION 2
Core Principles of Modules
A module is simply a JavaScript file that explicitly states what it shares with the outside world and what it keeps private. This concept rests on a few foundational ideas that make large codebases manageable.
Encapsulation
export are visible to other files. Everything else stays private, preventing accidental interference.Reusability
import it into any file in any project. Write once, use everywhere.Separation of Concerns
Dependency Management
KEY TAKEAWAY
import statement — it connects the rooms together.SECTION 3
How Modules Connect — A Visual Map
The diagram below shows a small project split into four modules. Notice how app.js acts as the entry point, importing functionality from the other three files. Each module exports only what other files need, keeping internal details hidden.
app.js imports from three modules. Each module exports only specific functions (solid arrows) while keeping helper functions and internal variables private (dashed lines leading to the private zone).In the diagram, the solid colored arrows represent import statements — the explicit connections between files. The dashed lines below each module point to the private internals that other files can never access. This clear boundary between public and private is what makes modules so powerful for keeping code organized.
SECTION 4
How Import and Export Actually Work
JavaScript's module system revolves around two keywords: export and import. When you write export in front of a variable, function, or class, you are saying: "This is public — other files may use it." Everything else in the file remains private by default. To consume an exported value, another file uses import along with the file path.
Named Exports
A named export lets you export multiple values from a single file. Each value keeps its original name, and the importing file must use curly braces to pick out exactly what it needs.
NAMED EXPORT SYNTAX
The export keyword before the function declaration makes add available to other modules. Import it with: import { add } from './mathUtils.js';
Default Exports
A default export marks one value as the "main" thing a module provides. A file can have only one default export. When you import it, you can name it whatever you like — no curly braces needed.
DEFAULT EXPORT SYNTAX
Import it with any name: import logger from './logger.js'; — here logger refers to the default-exported function.
Re-exporting (Barrel Files)
As projects grow, you might create an index.js that re-exports items from several modules. This pattern is called a barrel file. It simplifies imports for consumers because they can pull everything from one path: export { add, subtract } from './mathUtils.js';.
HTML Tip
type="module" to your script tag: <script type="module" src="app.js"></script>. Without this attribute, the browser will not recognize import and export statements.SECTION 5
Organizing a Multi-File Project
Knowing the syntax is only half the story. Deciding how to organize your files matters just as much. A clear folder structure makes it easy for you — and anyone else reading your code — to find things quickly. Below is a typical structure for a small JavaScript project and a visual representation of how its modules relate to each other.
app.js imports from utility and service modules, and math.js itself imports from format.js.Grouping files by role — utilities for pure helper functions, services for data-fetching logic, and components for UI pieces — is a convention used in real-world projects at companies large and small. You are not forced to follow this exact layout, but having any consistent convention is far better than tossing every file into a single flat directory.
One Function, One File?
add, subtract, multiply), keep them in one module. If you find a file growing past 200–300 lines, consider splitting it.SECTION 6
Worked Example — Building a Mini Calculator App
Let's walk through creating a tiny calculator application using three separate module files. We will write the utility module, an output module, and the main entry file step by step.
Mini Calculator with ES Modules
- 1
Step 1 — Create the math utility module
In a file calledsrc/utils/math.js, define and export two functions using named exports:// src/utils/math.js export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; }Anything not exported — like a helper constant or internal function — stays private to this file.Two named exports are now available:addandmultiply. - 2
Step 2 — Create the display module
Insrc/display.js, create a default export — a single function that formats and logs results:// src/display.js export default function showResult(label, value) { console.log(`${label}: ${value}`); }One default export:showResult. - 3
Step 3 — Wire everything together in the entry file
Insrc/app.js, import from both modules and use them:// src/app.js import { add, multiply } from './utils/math.js'; import showResult from './display.js'; const sum = add(5, 3); const product = multiply(4, 7); showResult('Sum', sum); showResult('Product', product);Notice that named imports use curly braces ({ add, multiply }) while the default import does not (showResult).Running this file prints:Sum: 8Product: 28 - 4
Step 4 — Load it in the browser
Inindex.html, reference the entry file with thetype="module"attribute:<script type="module" src="src/app.js"></script>The browser readsapp.js, sees its import statements, and automatically fetchesmath.jsanddisplay.jsbefore running the code.Open the browser console to see the output. The three-file structure works seamlessly.
SECTION 7
Named vs. Default Exports — Strengths & Trade-offs
Both named and default exports accomplish the same fundamental goal — making code available to other files. However, they differ in important ways that affect readability, refactoring, and team collaboration. The table below highlights the key trade-offs.
| Feature | Named Export | Default Export |
|---|---|---|
| How many per file? | Unlimited — export as many values as you need | Exactly one per file |
| Import syntax | import { name } from '...' | import anyName from '...' |
| Rename at import? | Yes, using as keyword: import { add as sum } | Automatically — you choose any name |
| Editor autocomplete | Excellent — editors can look up exact names | Weaker — editors can't guess arbitrary names |
| Best for... | Utility libraries with many functions | Modules with one primary purpose (e.g., a class) |
Comparison of named and default export patterns
KEY TAKEAWAY
SECTION 8
Looking Ahead — Bundlers, Dynamic Imports & Beyond
The module syntax you've learned works perfectly for small-to-medium projects. As applications grow to hundreds of modules, developers introduce additional tools and techniques. This section previews some concepts you'll encounter in more advanced courses and real-world jobs.
| Concept | What You Know Now | What Comes Next |
|---|---|---|
| Loading modules | Browser loads each file separately via <script type="module"> | Bundlers (Webpack, Vite) combine all files into one optimized bundle for faster loading |
| Import timing | All imports run at the top of the file before any code executes (static) | Dynamic imports use import('./module.js') to load code on demand — great for lazy-loading |
| Sharing code | Copy files between projects manually | npm packages let you install and share modules from a global registry with millions of packages |
| Module style | ES Modules (import / export) | CommonJS (require()) is still common in older Node.js codebases — understanding both helps |
Current knowledge vs. advanced module concepts
Don't feel pressured to learn all of these right now. The fundamental pattern — export what you want to share, import what you need — remains exactly the same whether you're writing a three-file homework project or a three-thousand-file production application. Master the basics first, and the advanced tools will make much more sense when you're ready.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
Show answer
Question 2: BASIC CALCULATION
greet and farewell:
// messages.js
export function greet(name) {
return `Hello, ${name}!`;
}
export function farewell(name) {
return `Goodbye, ${name}!`;
}Show answer
import { greet, farewell } from './messages.js';. The names inside the braces must match the exported names exactly (unless you use the as keyword to rename). You can then call greet('Alice') and farewell('Bob') anywhere in the importing file.Question 3: INTERMEDIATE
calculateTax is not a function. Identify the bug and explain how to fix it.
// tax.js
export default function calculateTax(amount) {
return amount * 0.08;
}
// app.js
import { calculateTax } from './tax.js';
console.log(calculateTax(100));Show answer
tax.js, the function is exported as a default export, but app.js uses curly braces, which is the syntax for named imports. To fix it, remove the curly braces: import calculateTax from './tax.js';. Alternatively, you could change the export in tax.js to a named export by removing the default keyword.Question 4: APPLIED
app.js file.Show answer
gradeCalc.js exports calculateAverage and getLetterGrade as named exports — these are pure utility functions. Second, studentData.js exports a default array of student objects (names and scores). Third, display.js exports a named function printReport that formats output. In app.js you would write: import { calculateAverage, getLetterGrade } from './gradeCalc.js'; import students from './studentData.js'; import { printReport } from './display.js';. This keeps each concern separate and each file under 50 lines.Question 5: CRITICAL THINKING
Show answer
undefined values or runtime errors. Strategy one: extract the shared code into a third module C that both A and B import from, breaking the cycle. Strategy two: restructure your code so that only one module depends on the other, not both directions. A useful design principle is to think of your modules as a tree: data flows downward from the entry point, and no branch reaches back up. Keeping dependency graphs acyclic (no circles) is a hallmark of well-organized code.SUMMARY
Lesson Summary
Summary
JavaScript modules let you split a program across multiple files, where each file encapsulates its internal details and shares only selected values through export statements. Other files pull in those values with import statements, creating a clear dependency map. Named exports use curly braces and allow multiple exports per file, while a default export marks one primary value and lets the importer choose any name.
Organizing files into folders by responsibility — such as utils, services, and components — keeps projects readable as they grow. Remember to add type="module" to your script tag in HTML. With these fundamentals in place, you are ready to explore bundlers, dynamic imports, and the massive ecosystem of npm packages that power modern web development.