Skip to main content

Command Palette

Search for a command to run...

JavaScript Modules - Complete story about JavaScript Modules

Published
5 min read
JavaScript Modules - Complete story about JavaScript Modules

In this article, we are going to have an in-depth discussion about JavaScript modules. Why do we actually need a module and all the related terminology that are connected with it, like import & export? So let's begin our discussion.

Need for JavaScript Modules:

JavaScript was first created to make web pages interactive & dynamic instead of static. But as the internet grew, applications became more complex, and JavaScript started being used in many areas, like web, mobile, and more.

With this growth, codebases also became bigger. Managing everything in a single file becomes very difficult, especially when there are thousands of functions and variables. It also becomes hard for new developers to understand the code, so maintenance becomes difficult.

To solve this problem, modular programming was introduced. In 2015, ES6 added native support for modules using import and export, making code more organized and easier to manage.

How we use Modules

Before going into the usage part, let's take some non-modular code function examples so we can later make them modular and see how they're used.

Without Module

// app.js

const taxRate = 0.1;

function calculateTotal(price) {
  return price + price * taxRate;
}

function formatPrice(amount) {
  return "₹" + amount.toFixed(2);
}

function printBill(price) {
  const total = calculateTotal(price);
  console.log("Total:", formatPrice(total));
}

printBill(100);

Now, as you can see here, all of the functions are written in the same file, and all the variables are also declared in the same file. For a small codebase, it is okay, but assume it has 2,000 lines of code. Difficult to reuse the function, so much back and forth, and no separation of concerns there.

Problem in this code

Explain why exactly the current code is bad:

  • Everything is tightly coupled

  • No clear responsibility (business logic + formatting mixed)

  • Hard to reuse formatPrice elsewhere

  • Risk of variable conflicts (taxRate reused somewhere else)

Now break it down to implement the module

Step 1: Create separate files

math.js

const taxRate = 0.1;

export function calculateTotal(price) {
  return price + price * taxRate;
}

format.js

export function formatPrice(amount) {
  return "₹" + amount.toFixed(2);
}

app.js, and the use case

import { calculateTotal } from "./math.js";
import { formatPrice } from "./format.js";

function printBill(price) {
  const total = calculateTotal(price);
  console.log("Total:", formatPrice(total));
}

printBill(100);

Due to this implementation, we can now reuse the CalculateTotal and formatPrice functions multiple times.

Import & Export Core Concepts

Named Exports

To export multiple things from a file, whenever you use them, make sure the called them by the same name. We can't change the name of a method or variable during import.

export function a() {}
export function b() {} // Function export
export const PI=3.14 // Variable Export 
import { a, b, PI } from "./file.js";
// You can't call a to apple during import

Default Export

This also works similarly, with a few differences. A single file only contains one default export. During import, you can give any name to that imported method or variable as you wish.

export default function greet() {}
import greetUser from "./file.js";

Modules are scoped

  • Variables inside modules are NOT global

  • They don’t pollute the global namespace

Modules are loaded once

  • The imported file runs only one time

  • Then cached, this is why shared state works across imports

Note: Key things to rember one module can actually use another module, and a single file can import multiple files as well. To understand this, let's take a few examples.

File Structure

app.js
billing.js   (depends on math.js)
math.js
format.js

math.js

export function addTax(price, taxRate) {
  return price + price * taxRate;
}

billing.js (Depends on math.js)

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

export function getFinalAmount(price) {
  const taxRate = 0.1;
  return addTax(price, taxRate);
}

format.js (Independent module)

export function formatPrice(amount) {
  return "₹" + amount.toFixed(2);
}

app.js (Imports 2 modules)

import { getFinalAmount } from "./billing.js";
import { formatPrice } from "./format.js";

const price = 100;

// Uses billing (which internally uses math)
const finalAmount = getFinalAmount(price);

// Uses another independent module
console.log("Final Price:", formatPrice(finalAmount));

Benefits of Modular Code

Maintainability

  • Assume we are checking if a user is logged in with a single script. In a large code file, changing the login logic requires locating the specific method. With modular syntax, we simply update the user file, handling all related operations.

Reusability

  • We can reuse the modules in multiple areas, for example user logged in is a thing that we want to check in multiple pages.

Team Collaboration

  • Due to the modular code approach codebase becomes easy to understand, which helps new developers to collaborate quickly.

Scalability

  • If the size of the codebase increases over time, in a modular approach, it is easy to find bugs and errors.

conclusion

JavaScript modules help you split your code into smaller, manageable pieces. By using export and importYou can reuse functionality, keep your code organized, and avoid confusion as your project grows.