Skip to main content

Command Palette

Search for a command to run...

JavaScript Modules : Import and Export

Understanding JavaScript Modules : Import & Export

Published
β€’8 min read
JavaScript Modules : Import and Export
P

My name is 𝐏𝐫𝐚𝐀𝐚𝐬𝐑 and I talk about 𝗧𝗲𝗰𝗡-𝐊𝐧𝐨𝐰π₯𝐞𝐝𝐠𝐞, π—ͺπ—²π—―π——π—²π˜ƒ, π——π—²π˜ƒπ—’π—½π˜€ and π—Ÿπ—Άπ—³π—²π˜€π˜π˜†π—Ήπ—².

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:

JavaScript

Part 2 of 8

In this series of blogs I have written some interesting concepts related to JavaScript.

Up next

Understanding Objects in JavaScript

Understand Objects in JavaScript with very easy way .

More from this blog

Learn with Prakash

33 posts

My name is 𝐏𝐫𝐚𝐀𝐚𝐬𝐑 and I talk about 𝗧𝗲𝗰𝗡-𝐊𝐧𝐨𝐰π₯𝐞𝐝𝐠𝐞, π—ͺπ—²π—―π——π—²π˜ƒ, π——π—²π˜ƒπ—’π—½π˜€ and π—Ÿπ—Άπ—³π—²π˜€π˜π˜†π—Ήπ—².