JavaScript Modules : Import and Export
Understanding JavaScript Modules : Import & Export

So you've been writing JavaScript for a while now. Maybe you started with a single script tag in your HTML, and everything was fine β until it wasn't.
Your index.js became 800 lines long. You had functions named doStuff() and handleThing() that you were scared to delete because you didn't know what depended on them. Variables were clashing. You were scrolling forever just to find the function you wrote last week.
Sound familiar?
This is exactly the problem JavaScript modules were built to solve.
Why Modules Are Needed
Imagine building a house and putting every single thing β furniture, plumbing, wiring, walls β in one giant room. Technically it might work, but it would be a nightmare to maintain, fix, or extend.
That's what happens when all your JavaScript lives in one file.
Here's a real-world scenario. You're building a web app that handles user authentication, fetches data from an API, formats dates, and handles UI interactions. Without modules, your file might look like this:
Everything dumped in one file β auth logic, API calls, date formatting, UI code... 1,200 lines later...
You can't reuse any of it easily. If your teammate wants to use your formatDate() function in another project, they'd have to copy-paste it manually. If you rename a variable, you might break something three screens away. Testing becomes a guessing game.
Modules fix this by letting you split your code into small, focused files β each doing one job well. Then you connect them together using import and export.
Exporting β Sharing Code from a Module
When you want to make something available from a file, you export it.
Think of a file as a shop. By default, everything inside that shop is private β customers can't see it. Exporting is like putting something in the display window so others can use it.
There are two ways to export in JavaScript:
1. Named Exports
You can export multiple things from a single file by name.
mathUtils.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
Each of these is exported individually, and each has its own name. You can have as many named exports as you want in a file.
2. Default Export
A default export is the "main thing" a file exports. There can only be one default export per file.
greet.js
export default function greet(name) {
return Hello, ${name}!;
}
Think of it as the star product of the shop β the one thing that represents what this file is primarily about.
Importing β Using Code from Another Module
Once something is exported, you can import it into another file and use it.
Importing Named Exports
When you import named exports, you use curly braces and the exact name used during export.
app.js
import { add, subtract, PI } from './mathUtils.js';
console.log(add(5, 3)); //Output : 8
console.log(subtract(10, 4)); // 6
console.log(PI); // 3.14159
The names inside the curly braces must match the exported names exactly. This is important.
Importing Default Exports
When importing a default export, you don't use curly braces, and you can give it any name you like.
app.js
import greet from './greet.js';
console.log(greet('Arjun')); // Hello, Arjun!
Since there's only one default export per file, JavaScript knows exactly what you're asking for.
Importing Everything
If you want to import all named exports from a file at once, you can use the * syntax and give it a namespace:
import * as MathUtils from './mathUtils.js';
console.log(MathUtils.add(2, 3)); // 5
console.log(MathUtils.PI); // 3.14159
Renaming Imports
You can also rename named imports to avoid conflicts:
import { add as addNumbers } from './mathUtils.js';
console.log(addNumbers(1, 2)); // 3
Default vs Named Exports β Which One to Use?
This is a common question, and the short answer is: it depends on what the file is for.
Use named exports when:
Your file exports multiple related utilities (like a math or string helper file)
You want callers to be explicit about what they're using
You're building a library or utility module
stringUtils.js
export function capitalize(str) { ... }
export function trim(str) { ... }
export function slugify(str) { ... }
Use default exports when:
Your file represents one main thing β a component, a class, a single function
You want flexibility in naming on the consumer side
UserCard.js
export default function UserCard({ name, email }) { ... }
Can you use both?
Yes! A file can have both named exports and a default export.
api.js
export const BASE_URL = 'https://api.example.com';
export const TIMEOUT = 5000;
export default async function fetchUser(id) {
const response = await fetch(\({BASE_URL}/users/\){id});
return response.json();
}
Then you import them like this:
import fetchUser, { BASE_URL, TIMEOUT } from './api.js';
A Real-World Example
Let's put it all together with a mini project structure.
Imagine you're building a simple blog app. Here's how you'd split it into modules:
File: utils/formatDate.js
export function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-IN', { year: 'numeric', month: 'long', day: 'numeric', });
}
File: api/posts.js
const API_URL = 'https://jsonplaceholder.typicode.com';
export async function fetchPosts() {
const res = await fetch(${API_URL}/posts);
return res.json();
}
export async function fetchPostById(id) {
const res = await fetch(\({API_URL}/posts/\){id});
return res.json();
}
File: components/renderPost.js
import { formatDate } from '../utils/formatDate.js';
export default function renderPost(post) {
return <article>
<h2>${post.title}</h2>
<p>${post.body}</p>
<small> Posted on: ${formatDate(post.date)}</small>
</article> ;
}
File: app.js (main entry point)
import { fetchPosts } from './api/posts.js';
import renderPost from './components/renderPost.js';
async function init() {
const posts = await fetchPosts();
const container = document.getElementById('app'); container.innerHTML = posts.map(renderPost).join('');
}
init();
Each file has a single clear job. formatDate.js handles dates. posts.js handles API calls. renderPost.js renders HTML. app.js connects everything.
This is the power of modules.
Benefits of Modular Code
Let's make it crystal clear why you should care.
1. Maintainability When a bug appears in date formatting, you go straight to formatDate.js. You don't dig through 900 lines of mixed code. Each file has one job, so you always know where to look.
2. Reusability Your formatDate.js can be imported in ten different files across your project β or even in a completely different project. Write once, use everywhere.
3. Avoiding Naming Conflicts In old-school JavaScript without modules, all variables and functions lived in the global scope. Two libraries using a variable called data would clash. With modules, each file has its own scope. There's no pollution, no conflicts.
4. Better Collaboration In a team, different developers can work on different modules without stepping on each other's code. Merge conflicts become rare because everyone is working in their own file.
5. Easier Testing Small, focused modules are easy to test in isolation. You can import just formatDate.js and write unit tests for it without bringing in the entire application.
6. Lazy Loading (Performance) Modern bundlers and browsers can load modules on demand β only when they're needed. This means your app doesn't have to load everything upfront, leading to faster initial load times.
How to Use Modules in the Browser
If you're running JavaScript directly in the browser (without a bundler like Webpack or Vite), just add type="module" to your script tag:
That's it. The browser will handle the imports and exports natively.
Note: Modules must be served over a server (even a local dev server). They won't work if you just open an HTML file directly in your browser because of CORS restrictions. Use VS Code's Live Server extension or npx serve during development.
Quick Reference β Cheat Sheet
NAMED EXPORT: export function myFunction() {} export const myValue = 42;
NAMED IMPORT: import { myFunction, myValue } from './myFile.js';
DEFAULT EXPORT: export default function myFunction() {}
DEFAULT IMPORT: import myFunction from './myFile.js';
IMPORT ALL: import * as MyModule from './myFile.js';
RENAME ON IMPORT: import { myFunction as renamed } from './myFile.js';
MIXED (default + named): import defaultExport, { namedExport } from './myFile.js';
Wrapping Up
JavaScript modules aren't just a syntax feature β they're a mindset shift. They push you to think about your code in terms of small, independent, reusable pieces rather than one giant tangled mess.
Start small. Next time you write a utility function, put it in its own file and export it. Import it where needed. As your project grows, you'll thank yourself for this habit.
The best codebases aren't the ones with the cleverest tricks β they're the ones where every file does exactly one thing, and does it well.
That's what modules give you.
I write articles on blog.prakashtsx.com and also post development-related content on:



