SECTION 1
Historical Context & Motivation
In the early days of the web, JavaScript was used for tiny scripts — a form validation here, an animated button there. Developers would dump all their code into a single <script> tag, and that was fine when files were only fifty lines long. But as websites became full-blown applications with thousands of lines of logic, that single-file approach became a nightmare. Imagine writing an entire essay in one enormous paragraph with no headings, no chapters, and no sections — that's what early JavaScript projects felt like.
The core problem was code organization. Without a way to split code across files and share specific pieces between them, developers faced naming collisions (two variables with the same name clobbering each other), tangled dependencies, and bugs that were incredibly hard to track down. The JavaScript community spent years inventing solutions, eventually arriving at the ES Modules system we use today.
1995 • JavaScript Is Born
<script> tags in HTML files with no module system at all.2009 • CommonJS & Node.js
require() and module.exports, proving that modular JavaScript is practical.2015 • ES6 Modules Standardized
import and export keywords to the language specification, giving JavaScript a native module system.2018–Now • Broad Browser & Runtime Support
The central question this lesson addresses is straightforward: How do you break a growing JavaScript project into separate files, and how do those files share code with each other? The answer lies in understanding the export and import keywords.
SECTION 2
Core Principles of Modules
A module is simply a JavaScript file that explicitly declares what it shares with the outside world and what it keeps private. Think of each module as a room in a house: the room has its own furniture and decorations (private variables and helper functions), but it has a door that lets specific items pass through to other rooms. The export keyword is that door going out, and import is how another room reaches through the door to grab what it needs.
Encapsulation
Named Exports
export keyword before each declaration, or by listing them in an export statement at the bottom. The importing file picks them by name using curly braces.Default Export
export default. This is the 'main thing' the module provides. The importing file can give it any name it likes — no curly braces needed.Dependency Graph
KEY TAKEAWAY
SECTION 3
Visual Explanation — How Files Connect
The diagram below shows a small project with three files. The math.js module exports two functions. The greet.js module exports one default function. Finally, app.js imports from both modules and uses them together. Notice how each module keeps its internal helpers private.
math.js; the amber arrow shows the default import from greet.js. Private helpers like validate and PREFIX stay hidden inside their modules.The key insight from this diagram is that each file controls exactly what leaves its boundary. The validate function in math.js is a private helper — it does work internally but is never exported, so app.js cannot access it. This is encapsulation in action, and it protects your code from accidental interference.
SECTION 4
How Import and Export Actually Work
Let's look at the exact syntax for both types of exports and imports. Understanding these patterns is essential because mixing them up is one of the most common beginner mistakes.
Named Exports
A named export attaches a specific name to the value you're sharing. You can export inline — right where the function or variable is declared — or you can gather all exports at the bottom of the file. Here is the inline approach:
INLINE NAMED EXPORT
The export keyword goes directly before the declaration. The function is both created and exported in one statement.
And here is the gathered-at-the-bottom approach, which some developers prefer because it creates a clear manifest of everything a module shares:
GROUPED NAMED EXPORT
The curly braces list every name to export. The functions add and multiply must already be declared somewhere above this line.
Default Exports
A default export signals that the module has one primary value to share. A module can have only one default export, though it can also have additional named exports alongside it.
DEFAULT EXPORT
The default keyword means this function is the module's 'main attraction.' When importing, you don't need curly braces and you can choose any name.
Import Syntax
IMPORTING NAMED EXPORTS
Curly braces are required, and the names inside must match exactly what was exported. The path './math.js' is a relative file path.
IMPORTING A DEFAULT EXPORT
No curly braces. The name greet could be anything you like — e.g., import sayHello from './greet.js' would work just as well.
Common Mistake
SECTION 5
Organizing a Project's File Structure
Knowing the syntax is half the battle; the other half is deciding how to split your code into files in the first place. A good rule of thumb is one responsibility per file. If a file handles math utilities, it shouldn't also handle greeting messages. Keeping each file focused makes your code easier to read, test, and reuse.
.js file has a single responsibility. The HTML file loads only app.js as a module; the browser follows the import chain to load the rest.Notice the type="module" attribute on the script tag in index.html. This is what tells the browser to treat your JavaScript as an ES Module rather than a traditional script. Without it, import and export will throw a syntax error. When the browser loads app.js as a module, it reads the import statements at the top, fetches those files, reads their imports, and so on until every dependency is resolved.
Tip: Live Server
index.html and open it as a local file. Use the "Live Server" extension in VS Code, or run npx serve . in your terminal.SECTION 6
Worked Example — Building a Mini Calculator
Let's walk through creating a tiny project from scratch. We'll build a calculator that adds and multiplies, with a greeting message, spread across three files.
Mini Calculator with Modules
- 1
Step 1 — Create math.js with named exports
Create a file calledmath.js. Inside, define and export two functions:export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; }Two named exports:addandmultiply - 2
Step 2 — Create greet.js with a default export
Creategreet.jswith a single default export:export default function greet(name) { return `Welcome, ${name}! Ready to calculate?`; }One default export:greet - 3
Step 3 — Create app.js as the entry point
Createapp.jsand import from both modules:import { add, multiply } from './math.js'; import greet from './greet.js'; console.log(greet('Alex')); console.log('3 + 5 =', add(3, 5)); console.log('4 × 7 =', multiply(4, 7));Named imports use{ }; the default import has no braces. - 4
Step 4 — Link in HTML with type="module"
Inindex.html, add the script tag:<script type="module" src="./app.js"></script>Thetype="module"attribute is essential for browser-based ES Modules. - 5
Step 5 — Run and verify in the console
Open the page in a browser via a local server and check the developer console. You should see:Welcome, Alex! Ready to calculate? 3 + 5 = 8 4 × 7 = 28All three outputs appear — the modules are properly connected.
SECTION 7
Named vs. Default Exports — When to Use Each
One of the most frequent questions beginners ask is: "Should I use a named export or a default export?" Both are valid, and many real projects use a mix of both. The table below compares them side by side to help you decide.
| Feature | Named Export | Default Export |
|---|---|---|
| How many per file? | Unlimited — export as many as you like | Exactly one per file |
| Import syntax | import { name } from '...' | import anyName from '...' |
| Renaming on import | Use as keyword: import { add as sum } | Automatic — just pick any name |
| Best for… | Utility libraries with many functions (e.g., math helpers) | Files with one main purpose (e.g., a component or class) |
| Auto-import support | Editors auto-complete the exact name | Editors may not auto-suggest the correct name |
Comparison of named and default exports in ES Modules
PRACTICAL GUIDELINE
SECTION 8
Connection to Advanced Concepts
The import/export system you're learning now is the foundation for virtually every advanced JavaScript tool. As you progress, you'll encounter bundlers (like Webpack and Vite) that combine your many modules into optimized files for production, frameworks (like React and Vue) that rely on modules for component architecture, and package managers (like npm) that let you import code written by other developers from a global registry.
| What You Know Now | Where It Leads |
|---|---|
import { x } from './file.js' | import React from 'react' — importing from installed packages |
| Splitting code into 3–5 files | Real apps have hundreds of modules, organized into folders by feature |
Static import at the top of a file | Dynamic import() that loads modules on demand for performance |
Using type="module" in HTML | Bundlers handle this automatically; you just write import/export and they do the rest |
How today's skills connect to advanced JavaScript development
Don't worry about mastering all of these tools right now. The important takeaway is that the import/export pattern is universal in modern JavaScript. Whether you're building a simple calculator or a full web application, you'll always be organizing your code into modules. Getting comfortable with the syntax now will pay dividends in every future project.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
Show answer
as). A default export represents the single primary value of a module, so the importing file can use any name it chooses without curly braces. The syntax differs because the browser needs to know whether to look up a specific name in the module's export list (named) or simply grab the one value marked as default.Question 2: BASIC CALCULATION
utils.js file, write the correct import statement in app.js to use both functions:
// utils.js
export function double(n) { return n * 2; }
export function triple(n) { return n * 3; }Show answer
import { double, triple } from './utils.js';. You could then call double(5) to get 10 and triple(5) to get 15. If you only needed one of them, you could import just that one, e.g. import { double } from './utils.js';.Question 3: INTERMEDIATE
// logger.js
export default function log(msg) {
console.log(`[LOG]: ${msg}`);
}
// app.js
import { log } from './logger.js';
log('Starting app');Show answer
log is a default export, it should be imported without curly braces: import log from './logger.js';. Using curly braces tells JavaScript to look for a named export called log, which doesn't exist — only the default export exists. This is one of the most common mistakes when working with modules, so always check whether the source file uses export default or plain export.Question 4: APPLIED
questions.js file that exports an array of question objects and a score.js file that exports a function calculateScore which takes an array of answers and returns a number. Write the export statements for each file and the import statements you'd put in app.js.Show answer
questions.js, the array is the main value, so a default export makes sense: const questions = [ { q: 'What is 2+2?', a: 4 } ]; export default questions;. In score.js, you could use a named export since you might add more scoring utilities later: export function calculateScore(answers) { /* logic */ }. In app.js: import questions from './questions.js'; import { calculateScore } from './score.js';. Note how the default import (questions) has no braces while the named import (calculateScore) does.Question 5: CRITICAL THINKING
Show answer
math.js immediately tells you what to expect, whereas scrolling through 200 lines of mixed concerns is slower and more error-prone. Third, modules enable reusability: if you write a utility function in its own module today, you can import it into your next project with zero copy-pasting. Finally, learning modular organization now prepares you for larger codebases where a single-file approach simply won't scale.SUMMARY
Lesson Summary
Summary
JavaScript modules let you organize a project into multiple files, each with its own scope. You share values between files using export (to send values out) and import (to pull values in). Named exports allow multiple values per file and require curly braces on import, while a default export represents a file's main value and is imported without braces.
To use modules in the browser, your <script> tag must include type="module". A well-organized project follows the principle of one responsibility per file, keeping math helpers separate from greeting logic and DOM manipulation. This pattern of modular organization is the foundation for every modern JavaScript framework, bundler, and large-scale application you'll encounter in the future.