JS Modules Best Practices

Organize code into reusable, maintainable modules.

import/exportstructuremaintainability

Table of Contents

JavaScript Modules Tutorial

JavaScript modules allow you to split your code into separate files, making it more organized, reusable, and maintainable. ES6 modules are natively supported in all modern browsers.

JavaScript modules architecture showing import export flow between reusable files
Overview of JavaScript module structure using import and export patterns.

What Are JavaScript Modules?

Modules are discrete JavaScript files that can export functions, objects, or values for use in other files, and import functionality from other modules. Think of modules as building blocks where each file contains one focused piece of functionality.

Why Use Modules?

  • Encapsulation: Each module has its own scope.
  • Reusability: Write once, use in multiple files.
  • Maintainability: Easier debugging and updates in isolated units.
  • Dependency management: Clear relationships between files.

Setting Up Modules

Use type="module" in your script tag.

<script type="module" src="main.js"></script>
<script type="module">
  // Module code here
</script>

Important: Modules are deferred by default and should run over HTTP(s), not file://. Use a local server for testing.

Exporting Code from Modules

1. Individual (Inline) Exports

// math.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export class Calculator { multiply(a, b) { return a * b; } }

2. Unified Exports

// math.js
const PI = 3.14159;
function add(a, b) { return a + b; }
class Calculator { multiply(a, b) { return a * b; } }
export { PI, add, Calculator };

3. Default Exports

// utils.js
export default function formatCurrency(amount) {
  return `$${amount.toFixed(2)}`;
}

Importing Code into Modules

Import Named Exports

import { PI, add } from "./math.js";
import { add as sum, Calculator as Calc } from "./math.js";

Import Default Exports

import formatMoney from "./utils.js";

Import Everything (Namespace)

import * as mathUtils from "./math.js";
console.log(mathUtils.add(5, 3));

Combine Named + Default

import greet, { version } from "./my-module.js";

Dynamic Imports

Load heavy modules only when needed for performance.

button.addEventListener("click", async () => {
  const module = await import("./heavy-module.js");
  module.doSomething();
});

Working Example: A Shopping Cart Module System

Project Structure

cart-app/
├── index.html
├── main.js
└── modules/
    ├── products.js
    ├── cart.js
    └── utils.js

Step 1: products.js

const products = [
  { id: 1, name: "JavaScript Book", price: 29.99 },
  { id: 2, name: "Coffee Mug", price: 12.99 },
  { id: 3, name: "Hoodie", price: 49.99 }
];
export function getProduct(id) { return products.find(p => p.id === id); }
export function getAllProducts() { return [...products]; }
export default products;

Step 2: utils.js

export function formatPrice(amount) { return `$${amount.toFixed(2)}`; }
export function calculateDiscount(price, percentage) {
  return price - (price * percentage / 100);
}

Step 3: cart.js

import products from "./products.js";
let cartItems = [];
export function addToCart(productId, quantity = 1) {
  const product = products.find(p => p.id === productId);
  if (!product) return false;
  const existing = cartItems.find(item => item.id === productId);
  if (existing) existing.quantity += quantity;
  else cartItems.push({ ...product, quantity });
  return true;
}
export function getCartTotal() {
  return cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

Step 4: main.js

import { getAllProducts } from "./modules/products.js";
import { addToCart, getCartTotal, getCartCount, getCartItems } from "./modules/cart.js";
import { formatPrice } from "./modules/utils.js";

window.addToCartHandler = productId => {
  addToCart(productId);
  updateCartDisplay();
};

Step 5: index.html

<!DOCTYPE html>
<html>
<head><title>Shopping Cart</title></head>
<body>
  <h2>Products</h2>
  <div id="products"></div>
  <h2>Cart (<span id="cart-count">0</span>)</h2>
  <div id="cart"></div>
  <h3>Total: <span id="cart-total">$0.00</span></h3>
  <script type="module" src="main.js"></script>
</body>
</html>

Important Notes

File Extensions

import { add } from "./math.js"; // Correct
// import { add } from "./math";  // Fails in native browser ESM

Module Scope

// secret.js
const secretKey = "abc123";
export function getSecret() { return secretKey; }

Node.js

In Node.js, enable ES modules with "type": "module" in package.json or by using .mjs files.

Common Mistakes to Avoid

  • Forgetting type="module" in script tags.
  • Using wrong relative paths (use ./ when needed).
  • Missing .js extension in native browser imports.
  • Creating circular dependencies between modules.
  • Running modules directly from file:// instead of a local server.

Related: ES6 Features, Async JavaScript.

10 Modules Interview Q&A

10 Modules MCQs