JavaScript Modules: Import and Export Explained
Modern JavaScript applications don't stay small for long. A few functions quickly turn into dozens of files and without proper structure, things become seriously messy. This is exactly where modules come in.
This article breaks down why modules exist, how to use import and export and why modular code makes our life easier as a developer.
The Problem: Code Organization without modules
Imagine writing everything in a single file,
// app.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function formatCurrency(value) {
return `$${value}`;
}
console.log(formatCurrency(add(2, 3)));
This woks fine... until,
The file grows to 1000+ lines
Multiple developers start working on it
We accidentally reuse the same variable or function names
Debugging becomes painful
What are the main issues?
No separation of concerns
Hard to maintain
Poor readability
High risk of naming conflicts
Now we have the solution : JavaScript modules
Modules let us split code into multiple files, each responsible for a specific task. Instead of one big file, we organize like this,
math.js
currency.js
app.js
Each file becomes a module with its own scope.
Exporting Functions or Values
To use code from one file in another, we first need to export it.
Named Exports
// math.js
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
We can export multiple things from a file.
we can also export variables:
export const PI = 3.14;
Importing Modules
import { add, multiply } from './math.js';
console.log(add(2, 3)); // 5
console.log(multiply(2, 3)); // 6
Note:
Use { } for named imports
The names must match exactly
Default vs Named Export
Default Export
A file can have one defalut export.
// logger.js
export default function log(message) {
console.log(message);
}
Importing default export:
import log from "./logger.js"
log("Hello World")
No { } needed & we can rename it.
import customLogger from "logger.js;
Named Export (Recap)
// utils.js
export function sum(a, b) {
return a + b;
}
Import:
import { sum } from './utils.js';
Defalut vs Named: When to use What?
| Use Case | Recommended |
|---|---|
| Single main functionality | Default export |
| Multiple utilities/functions | Named exports |
Combining both
We can mix both in one file:
// user.js
export const role = "admin";
export default function getUser() {
return {name: "Elon Musk"}
}
Import:
import getUser, { role } from './user.js';
How Modules Improve Maintainability
Clear separation of concerns
math.js --> math logic
auth.js --> authentication
db.js --> databaseEach file has a single responsibility
Easier Debugging
If something breaks in math.js, we don't search the whole project.Reusability
We can reuse modules across files:
import { add } from './math.js';
write once, use anywhere.Avoid Global Scope Pollution
Modules are scoped by default.
No more accidental overrides likevar count = 10; // global problemBetter Collaboration
Teams can work on different modules independently without conflicts.
Small Real-World Example
// tax.js
export function calculateTax(amount) {
return amount * 0.18;
}
// invoice.js
import { calculateTax } from './tax.js';
export function generateInvoice(amount) {
const tax = calculateTax(amount);
return amount + tax;
}
// app.js
import { generateInvoice } from './invoice.js';
console.log(generateInvoice(100));
Clean, readable, and scalable.
Standout Insight: A Common Mistake
Many beginners overuse default exports.
Problem:
// bad practice in large codebases
export default function() {}
When importing
import anything from './file.js';
This leads to inconsistent naming across the project.
Better Approach
Prefer named exports in shared utilities.
Use default export only when the module has a single clear responsibility.