# JavaScript Modules: Import and Export Explained

As your JavaScript projects grow, keeping all code in a single file becomes a nightmare. Functions pile up, variables clash, and it becomes impossible to find anything. Modules solve this by letting you split code into separate files, each responsible for one thing.

In this article we will cover how to organize JavaScript using modules, understand exports and imports, and learn the difference between default and named exports.

* * *

## Why Modules Are Needed

### The Problem: Everything in One File

Imagine a project that grows to 5000 lines in a single `app.js` file:

```javascript
// app.js — 5000 lines of chaos

function calculatePrice(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

function formatCurrency(amount) {
  return "$" + amount.toFixed(2);
}

function sendEmail(to, subject, body) {
  // Email logic here
}

function validateForm(data) {
  // Validation logic here
}

function connectDatabase(uri) {
  // Database logic here
}

// ... 4900 more lines
```

Problems:
- Hard to find what you need in 5000 lines
- Global namespace pollution — all functions are global
- Name collisions — someone else might have a `formatDate()` function
- No clear responsibility — which functions belong together?
- Difficult to test — can't test one feature in isolation
- Can't reuse code across projects easily

### The Solution: Split Into Modules

```
project/
  ├── utils/
  │   ├── price.js       (price calculation)
  │   └── currency.js    (currency formatting)
  ├── email/
  │   └── sender.js      (email logic)
  ├── validation/
  │   └── form.js        (form validation)
  ├── database/
  │   └── connection.js  (database logic)
  └── app.js             (brings it all together)
```

Now each file has a clear purpose. Code is organized, reusable, and testable.

* * *

## Exporting Functions or Values

### Named Exports

Use `export` to make functions, variables, or classes available to other files.

**utils/price.js:**

```javascript
export function calculatePrice(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

export function applyDiscount(price, discount) {
  return price * (1 - discount);
}

export const TAX_RATE = 0.1;
```

You can export multiple things from one file. Each export gets a name.

### Default Exports

A default export is the main thing a module does. Each module can have only one default export.

**utils/currency.js:**

```javascript
export default function formatCurrency(amount) {
  return "$" + amount.toFixed(2);
}
```

Default exports are useful when a module has one primary purpose.

### Mixing Default and Named Exports

You can have one default and multiple named exports in the same file:

**email/sender.js:**

```javascript
export default function sendEmail(to, subject, body) {
  console.log(`Email sent to ${to}: ${subject}`);
}

export function sendBulkEmail(recipients, subject, body) {
  recipients.forEach(to => sendEmail(to, subject, body));
}

export const EMAIL_TEMPLATES = {
  welcome: "Welcome to our app!",
  reset: "Click here to reset your password"
};
```

* * *

## Importing Modules

### Importing Named Exports

Use curly braces `{}` to import specific named exports:

**app.js:**

```javascript
import { calculatePrice, applyDiscount, TAX_RATE } from "./utils/price.js";

const items = [
  { name: "Apple", price: 2 },
  { name: "Banana", price: 1.5 }
];

let total = calculatePrice(items);
console.log("Total:", total); // 3.5

let discounted = applyDiscount(total, 0.2);
console.log("After 20% discount:", discounted); // 2.8

console.log("Tax rate:", TAX_RATE); // 0.1
```

The names in the curly braces must match the exports exactly.

### Importing Default Exports

Default exports can be imported with any name:

**app.js:**

```javascript
import formatCurrency from "./utils/currency.js";

let total = 99.99;
console.log(formatCurrency(total)); // "$99.99"
```

Notice no curly braces. The name `formatCurrency` could be anything — it matches the default export regardless of what the file calls it internally.

### Importing Both Default and Named

```javascript
import sendEmail, { sendBulkEmail, EMAIL_TEMPLATES } from "./email/sender.js";

sendEmail("john@example.com", "Hello", "How are you?");
sendBulkEmail(["alice@example.com", "bob@example.com"], "Update", "Check this out");
console.log(EMAIL_TEMPLATES.welcome);
```

### Renaming Imports

If two modules export something with the same name, rename on import:

```javascript
import { formatCurrency as formatUSD } from "./utils/usd.js";
import { formatCurrency as formatEUR } from "./utils/eur.js";

console.log(formatUSD(100));   // $100.00
console.log(formatEUR(100));   // €100,00
```

* * *

## Default vs Named Exports

| Aspect | Default Export | Named Export |
| --- | --- | --- |
| Count per file | 1 maximum | Multiple |
| Syntax | `export default` | `export` |
| Import syntax | No braces | Curly braces |
| Renamed on import | Always | Optionally |
| Use case | Primary responsibility | Utilities and helpers |

### When to Use Each

**Use default export when:**
- The module has one clear purpose
- Someone will likely want the main thing it exports

```javascript
// logger.js — one job: create a logger
export default function createLogger(name) {
  return {
    log: (msg) => console.log(`[${name}] ${msg}`)
  };
}
```

**Use named exports when:**
- The module provides multiple related utilities
- Users might want some exports but not others

```javascript
// math.js — provides multiple math functions
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
export function multiply(a, b) { return a * b; }
```

* * *

## Module Import/Export Flow

How modules work together:

```plaintext
price.js
  |
  | exports: calculatePrice, applyDiscount, TAX_RATE
  |
app.js ----> imports: { calculatePrice, TAX_RATE }
  |
  ├---> uses calculatePrice()
  |
  └---> uses TAX_RATE

currency.js
  |
  | exports default: formatCurrency
  |
app.js ----> imports: formatCurrency
  |
  └---> uses formatCurrency()
```

Each file declares what it exports. Other files import only what they need.

* * *

## File Dependency Diagram

A larger project structure:

```plaintext
                    ┌─── database/connection.js
                    │
                    ├─── email/sender.js
                    │
                    ├─── utils/price.js
                    │
                    ├─── utils/currency.js
                    │
                    └─── validation/form.js
                            |
                            | (all imported by)
                            v
                        app.js (main file)
```

`app.js` is the entry point. It imports from other modules. Those modules are independent and can be tested separately.

* * *

## Benefits of Modular Code

### 1. Readability

Each file is focused. You open `price.js` and see only pricing logic:

```javascript
// price.js — 50 lines, clear purpose
export function calculatePrice(items) { ... }
export function applyDiscount(price, discount) { ... }
```

Much better than scrolling through 5000 lines.

### 2. Maintainability

Change pricing logic? Edit one file. No risk of breaking unrelated code.

```javascript
// Only price.js changes
export function calculatePrice(items) {
  // New logic here
  return items.reduce((sum, item) => sum + item.price * 1.05, 0);
}
```

### 3. Testability

Test each module independently:

```javascript
import { calculatePrice, applyDiscount } from "./utils/price.js";

// Test calculatePrice
const items = [{ price: 100 }];
console.assert(calculatePrice(items) === 100, "Price calculation failed");

// Test applyDiscount
console.assert(applyDiscount(100, 0.2) === 80, "Discount failed");
```

### 4. Reusability

Use the same module across multiple projects:

```javascript
// Project A: E-commerce app
import { calculatePrice } from "../shared/utils/price.js";

// Project B: Billing system
import { calculatePrice } from "../shared/utils/price.js";
```

Both projects share the same tested, reliable code.

### 5. Team Collaboration

Multiple developers work on different modules without stepping on each other:

```
Alice works on: email/sender.js
Bob works on: validation/form.js
Charlie works on: database/connection.js

All three changes merge without conflict
```

### 6. Namespace Control

No global variable pollution. Each module has its own scope:

```javascript
// module-a.js
let config = { ... };  // This config is private to module-a

// module-b.js
let config = { ... };  // Different config, no collision
```

* * *

## Complete Real-World Example

### Project Structure

```
project/
  ├── utils/
  │   ├── calculator.js
  │   └── formatter.js
  ├── services/
  │   └── api.js
  └── app.js
```

**utils/calculator.js:**

```javascript
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export function multiply(a, b) {
  return a * b;
}
```

**utils/formatter.js:**

```javascript
export default function formatResult(number) {
  return `Result: ${number}`;
}
```

**services/api.js:**

```javascript
export default async function fetchData(url) {
  const response = await fetch(url);
  return response.json();
}

export function buildURL(base, endpoint) {
  return `${base}/${endpoint}`;
}
```

**app.js (brings it all together):**

```javascript
import { add, subtract, multiply } from "./utils/calculator.js";
import formatResult from "./utils/formatter.js";
import fetchData, { buildURL } from "./services/api.js";

// Use calculator functions
const sum = add(10, 5);
console.log(formatResult(sum)); // Result: 15

const product = multiply(3, 4);
console.log(formatResult(product)); // Result: 12

// Use API utilities
const url = buildURL("https://api.example.com", "users");
console.log("Fetching from:", url);

fetchData(url).then(data => console.log("Data:", data));
```

Clear separation of concerns. Each module has one job.

* * *

## Importing All Exports

If a module has many named exports, import them all at once:

```javascript
import * as calculator from "./utils/calculator.js";

calculator.add(10, 5);      // 15
calculator.subtract(10, 5); // 5
calculator.multiply(10, 5); // 50
```

All exports become properties of the `calculator` object.

* * *

## Practice Assignment

Build a simple modular project:

**1. Create utils/string.js with string utilities:**

```javascript
export function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export function reverse(str) {
  return str.split("").reverse().join("");
}

export function countWords(str) {
  return str.split(" ").length;
}
```

**2. Create utils/array.js with array utilities:**

```javascript
export default function findMax(arr) {
  return Math.max(...arr);
}

export function findMin(arr) {
  return Math.min(...arr);
}

export function average(arr) {
  return arr.reduce((a, b) => a + b, 0) / arr.length;
}
```

**3. Create app.js that imports and uses both:**

```javascript
import { capitalize, reverse, countWords } from "./utils/string.js";
import findMax, { findMin, average } from "./utils/array.js";

console.log(capitalize("hello"));           // Hello
console.log(reverse("hello"));              // olleh
console.log(countWords("hello world test")); // 3

const numbers = [5, 2, 8, 1, 9];
console.log(findMax(numbers));  // 9
console.log(findMin(numbers));  // 1
console.log(average(numbers));  // 5
```

Try running this in Node.js (add `"type": "module"` to your `package.json`).

* * *

## Quick Recap

* **Modules** let you split code into separate files, each with a clear purpose
  
* Code in one file doesn't automatically pollute the global namespace
  
* **Named exports** let you export multiple things from a file using `export`
  
* **Default exports** represent the primary thing a module does using `export default`
  
* Import named exports with curly braces: `import { name } from "./file.js"`
  
* Import default exports without braces: `import name from "./file.js"`
  
* You can mix default and named exports in the same file
  
* Rename imports with `as`: `import { price as cost } from "./file.js"`
  
* Modules improve readability, testability, maintainability, and code reuse
  
* Each module has its own scope — no global variable collisions
  
* Use default exports for a module's primary responsibility
  
* Use named exports for related utilities and helpers

Modular code is the foundation of scalable JavaScript projects. Once you organize code into modules, large projects become manageable.

Happy coding! 🚀

---

> If you enjoyed this article, check out my other blogs on this profile.
> 🔗 Connect with me: [LinkedIn](https://linkedin.com/in/rajharsh03) | [GitHub](https://github.com/RajHarsh03) | [X (Twitter)](https://x.com/rajharsh03)
