Chapter 32 of 32

JavaScript Modules

As JavaScript applications grow, keeping everything inside one huge JavaScript file can quickly become difficult to manage.

Imagine a project with hundreds or thousands of lines of code containing:

  • User authentication

  • Shopping cart logic

  • API functions

  • Form validation

  • Utility functions

  • UI code

Putting everything into one file would make the project messy.

This is where JavaScript modules come in.

What is a JavaScript Module?

A module is a JavaScript file that contains code which can be exported and imported by other JavaScript files.

Instead of putting everything into one file, we can split our application into smaller, focused files.

For example:

project/
│
├── app.js
├── math.js
├── users.js
└── utils.js

Each file can contain code related to a particular responsibility.

math.js   → Mathematical functions
users.js  → User-related code
utils.js  → Utility functions
app.js    → Main application

This makes our code easier to understand, maintain, and reuse.


Why Do We Need Modules?

Without modules, a large application might look like this:

app.js
 ├── Login code
 ├── User code
 ├── Payment code
 ├── API code
 ├── Validation code
 ├── Utility functions
 └── UI code

As the project grows, finding and modifying code becomes difficult.

With modules:

auth.js      → Authentication
users.js     → Users
payments.js  → Payments
api.js       → API calls
validation.js → Validation
utils.js     → Utilities

Each file has a clearer purpose.

Some major benefits are:

  • Better code organization

  • Code reuse

  • Easier maintenance

  • Better separation of responsibilities

  • Avoiding unnecessary global variables


Exporting Code

Before another file can use something from a module, we need to export it.

Suppose we have a file called math.js:

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

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

Here, both functions are exported.

Now another JavaScript file can use them.


Importing Code

Suppose our main file is app.js.

We can import the functions like this:

import { add, subtract } from "./math.js";

console.log(add(10, 5));
console.log(subtract(10, 5));

Output:

15
5

The basic idea is:

math.js
   ↓
export
   ↓
app.js
   ↓
import
   ↓
Use the functions

Named Exports

When we export specific values by name, they are called named exports.

For example:

export const pi = 3.14159;

export function square(number) {
    return number * number;
}

We can import them:

import { pi, square } from "./math.js";

console.log(pi);
console.log(square(5));

Output:

3.14159
25

The names used during import must correspond to the exported names unless we rename them.


Renaming Named Imports

We can rename an imported value using as.

import { square as calculateSquare } from "./math.js";

console.log(calculateSquare(5));

Here:

square
   ↓
calculateSquare

The original exported function is still called square; we've simply given it another local name in this file.


Exporting at the End

We don't have to put export directly before every declaration.

For example:

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

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

export { add, subtract };

This does the same thing as:

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

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

Choose whichever style makes the module easier to read.


Default Export

A module can also have one default export.

For example:

export default function greet(name) {
    return `Hello, ${name}!`;
}

We can import it without curly braces:

import greet from "./greet.js";

console.log(greet("John"));

Output:

Hello, John!

Notice the difference:

// Named import
import { add } from "./math.js";

// Default import
import greet from "./greet.js";

Renaming a Default Import

Default imports can be given any local name.

For example:

import sayHello from "./greet.js";

This is perfectly valid even if the function was originally named greet.

That's because a module can have only one default export, and the importing file chooses the local name.


Named vs Default Exports

The difference is important:

Named Export

Default Export

Can have multiple

One default export per module

Uses {} when importing

Doesn't use {}

Import name normally matches export name

Import name can be chosen

export { add }

export default add

import { add } ...

import add ...

For example:

// Named
export function add() {}
// Import
import { add } from "./math.js";

And:

// Default
export default function add() {}
// Import
import add from "./math.js";

Importing Everything

We can import all named exports from a module using *.

For example:

import * as math from "./math.js";

console.log(math.add(10, 5));
console.log(math.subtract(10, 5));

Here, math becomes an object containing the exported members.

Conceptually:

math
├── add
└── subtract

This can be useful when a module contains a group of related functions.


Modules in HTML

When using JavaScript modules directly in a browser, we need to tell the browser that the script is a module.

<script type="module" src="app.js"></script>

Without type="module", browser JavaScript won't treat the file as an ES module.

Then app.js can import another module:

import { add } from "./math.js";

console.log(add(10, 20));

And math.js can contain:

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

Module File Paths

When importing local modules in the browser, it's common to specify the file path explicitly:

import { add } from "./math.js";

Here:

./math.js

means math.js is in the same directory.

If it's inside a folder:

import { add } from "./utils/math.js";

And:

../math.js

means the file is in the parent directory.


Modules Have Their Own Scope

One major benefit of modules is that variables declared inside a module aren't automatically placed into the global scope.

For example, math.js:

const secretNumber = 42;

export function getNumber() {
    return secretNumber;
}

Another file cannot directly access:

console.log(secretNumber);

because secretNumber belongs to the module's own scope.

It can access the value through the exported function:

import { getNumber } from "./math.js";

console.log(getNumber());

Output:

42

This helps keep internal implementation details private.


Reusing Modules

One of the biggest advantages of modules is code reuse.

Suppose we create:

utils.js
export function formatCurrency(price) {
    return `₹${price.toFixed(2)}`;
}

Now different files can reuse it.

import { formatCurrency } from "./utils.js";

console.log(formatCurrency(500));

Another file can also use the same function:

import { formatCurrency } from "./utils.js";

console.log(formatCurrency(1250));

Instead of rewriting the same function in multiple places, we define it once and reuse it.


A Practical Example

Imagine we're building a small shopping application.

We can organize it like this:

project/
│
├── app.js
├── cart.js
├── products.js
└── utils.js

products.js

export const products = [
    {
        name: "Laptop",
        price: 50000
    },
    {
        name: "Mouse",
        price: 1000
    }
];

cart.js

export function calculateTotal(cart) {
    return cart.reduce((total, product) => {
        return total + product.price;
    }, 0);
}

app.js

import { products } from "./products.js";
import { calculateTotal } from "./cart.js";

const total = calculateTotal(products);

console.log(`Total: ₹${total}`);

Now each file has a clear responsibility:

products.js
    ↓
Product data

cart.js
    ↓
Cart logic

app.js
    ↓
Application entry point

This structure becomes extremely useful as applications grow.


Modules and Third-Party Packages

The module system is also the foundation of modern JavaScript package management.

For example, in a Node.js or frontend project, you might see:

import express from "express";

or:

import React from "react";

Here, JavaScript is importing functionality provided by external packages.

Tools such as npm and modern build systems make it easy to manage these dependencies.

You don't need to understand package management yet. The important thing is to recognize that the same import and export concepts are used throughout the JavaScript ecosystem.

Conclusion

JavaScript modules allow us to split a large application into smaller, organized, reusable files.

The two most important concepts are:

Export

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

Import

import { add } from "./math.js";

You should also remember:

  • Named exports use {} when importing.

  • Default exports don't use {}.

  • A module can have multiple named exports but only one default export.

  • Modules have their own scope.

  • Modules make code easier to organize and reuse.

  • Browser modules are loaded using <script type="module">.

The basic flow is:

Create module
    ↓
Export code
    ↓
Import code
    ↓
Use it in another file

Once you understand modules, you're ready to work with larger JavaScript projects where code is divided into multiple files instead of keeping everything inside one giant script.