Skip to main content

Command Palette

Search for a command to run...

JavaScript Modules: Import and Export Explained

Updated
6 min read

If you've ever written a JavaScript file that kept growing and growing until you had no idea what was defined where — modules are the fix for that.


Why Modules Are Needed

Imagine you're building a food delivery app. You have code for user login, code for the menu, code for payments, and code for order tracking — all dumped into one giant file called app.js.

After a few weeks it looks like this:

C:\project> node app.js
app.js: 1,400 lines
Variables: 87
Functions: 63
Bugs: countless

You change one function and something completely unrelated breaks. A teammate edits the same file at the same time and your changes clash. You can't reuse the payment logic in another project without copying the whole mess.

This is the one big file problem. JavaScript modules solve it by letting you split your code into small, focused files — each doing one job — and connecting them together.


Exporting: Sharing Code From a File

By default, everything you write inside a JavaScript file is private to that file. Nothing leaks out. If you want another file to use something, you have to export it explicitly.

Think of it like a shop window. The shop has hundreds of products in the back, but only the ones placed in the window are available to customers outside.

Say you have a file called math.js:

C:\project> cat math.js

// These are just sitting inside the file — private
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const PI = 3.14159;

Right now, nothing is available to other files. To share them, you export:

C:\project> cat math.js

export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const PI = 3.14159;

Now add, subtract, and PI are available to anyone who imports this file.


Importing: Using Code From Another File

Once something is exported, any other file can pull it in using import.

C:\project> cat app.js

import { add, PI } from './math.js';

console.log(add(2, 3));   // 5
console.log(PI);          // 3.14159
C:\project> node app.js
5
3.14159

You only import what you actually need. If you don't need subtract, you simply don't include it. The rest of math.js stays out of your way.

Here's how the files connect:
math.js
exports: add, subtract,
└──► app.js (imports: add, PI)
└──► stats.js (imports: subtract)

Each file picks exactly what it needs. Nothing more.


Named Exports vs Default Exports

There are two styles of exporting in JavaScript. Understanding the difference saves a lot of confusion early on.

Named Exports

You've already seen these. You name each thing you export, and you import it using the exact same name inside curly braces { }.

C:\project> cat greet.js

export const sayHello = (name) => `Hello, ${name}!`;
export const sayBye = (name) => `Bye, ${name}!`;
C:\project> cat app.js

import { sayHello, sayBye } from './greet.js';

console.log(sayHello('Pushkar'));
console.log(sayBye('Pushkar'));
C:\project> node app.js
Hello, Pushkar!
Bye, Pushkar!

A file can have as many named exports as it wants.


Default Exports

Sometimes a file has one main thing it wants to share — one primary purpose. For that, you use export default.

C:\project> cat logger.js

const log = (message) => {
  console.log(`[LOG]: ${message}`);
};

export default log;
C:\project> cat app.js

import log from './logger.js';

log('Server started');
log('User logged in');
C:\project> node app.js
[LOG]: Server started
[LOG]: User logged in

Notice — no curly braces { } when importing a default export. And you can name it anything you want on the importing side:

import logger from './logger.js';      // works
import printMessage from './logger.js'; // also works
import log from './logger.js';         // also works

All three are importing the same default export — just giving it a different local name.


Named vs Default — Quick Comparison

Named Export Default Export ──────────────────────────────────────────────── export const add = ... export default add import { add } from ... import add from ... Curly braces required No curly braces File can have many File can only have ONE Name must match on import Name can be anything Best for: utility files Best for: main class/function


A Real Project Structure

Here's what a small project looks like once you start using modules properly:

C:\project> tree
│
├── app.js          ← entry point, pulls everything together
├── math.js         ← exports: add, subtract, multiply
├── logger.js       ← exports default: log
├── userService.js  ← exports: getUser, createUser
└── config.js       ← exports: API_URL, MAX_RETRIES
C:\project> cat app.js

import { add, multiply } from './math.js';
import log from './logger.js';
import { getUser } from './userService.js';
import { API_URL } from './config.js';

log('App started');
log(`API: ${API_URL}`);
log(`2 + 3 = ${add(2, 3)}`);
C:\project> node app.js
[LOG]: App started
[LOG]: API: https://api.example.com
[LOG]: 2 + 3 = 5

Each file has one job. app.js is just the conductor — it doesn't do math or logging itself, it just connects the pieces.


Benefits of Modular Code

Find things instantly

When a bug happens in payment logic, you open payment.js. Not a 1,400-line file. You know exactly where to look.

C:\project> node app.js
Error in payment processing

# With modules — you know exactly where to look:
C:\project> code payment.js   ← open just this file

Reuse across projects

Built a solid date formatting utility? Export it. Drop the file into your next project and import it. No rewriting.

# Project A
C:\projectA> import { formatDate } from './utils/date.js';

# Project B — same file, just copied over
C:\projectB> import { formatDate } from './utils/date.js';

Team-friendly

Two developers can work on userService.js and paymentService.js at the same time without touching each other's code.

# Developer 1
C:\project> git diff userService.js    ← changed only this

# Developer 2
C:\project> git diff paymentService.js ← changed only this

# No conflict. No stepping on each other.

Easy to test

Small, focused files are easy to test in isolation. You test math.js by itself — you don't need the whole app running.

C:\project> node test-math.js
add(2, 3) → 5 ✓
subtract(10, 4) → 6 ✓
multiply(3, 3) → 9 ✓
All tests passed.

The Big Picture

Before Modules After Modules ────────────────────────────────────────────────── One giant app.js Many small focused files Everything mixed together Each file has one job Hard to find anything Open the right file instantly Can't reuse code easily Import what you need, anywhere Team edits clash constantly Everyone works in their own file

Modules don't make your code run faster. They make your code manageable — especially as it grows. Start splitting files early, name them clearly, and export only what other files actually need.

Once you get used to thinking in modules, writing one big file starts to feel wrong — because it is.