JavaScript Modules — import and export

Modules allow JavaScript code to be split into separate files, each responsible for one specific task. Code can be exported from one file and imported into another. This keeps large projects organized, avoids naming conflicts, and makes code reusable.

Real-life analogy: Modules are like departments in a company. Each department (module) handles its own work. When another department needs something, it requests it — just like importing from a module.

Why Modules?

  • Organization — Large codebases are split into smaller, focused files
  • Reusability — A utility function written once can be imported anywhere
  • Encapsulation — Variables and functions in a module are private by default
  • No naming conflicts — Each module has its own scope

Enabling Modules in HTML

To use modules in a browser, the script tag must have type="module".

<!-- In your HTML file -->
<script type="module" src="main.js"></script>

Named Exports

Named exports allow multiple values to be exported from a single file. The same names must be used when importing.

File: mathUtils.js

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

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

export const PI = 3.14159;

File: main.js (importing)

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

console.log(add(10, 5));       // 15
console.log(subtract(10, 5));  // 5
console.log(PI);               // 3.14159

Renaming Imports with as

Use as to rename an import to avoid conflicts with existing variable names.

import { add as sum, subtract as minus } from "./mathUtils.js";

console.log(sum(8, 3));    // 11
console.log(minus(8, 3));  // 5

Default Export

A module can have one default export. The default export can be imported with any name.

File: greet.js

export default function greetUser(name) {
  return `Hello, ${name}! Welcome to eStudy247.`;
}

// Or export a class as default:
// export default class Timer { ... }

File: main.js (importing default)

import greetUser from "./greet.js";        // Can use any name
import welcome  from "./greet.js";         // Same function, different alias

console.log(greetUser("Pooja"));  // Hello, Pooja! Welcome to eStudy247.
console.log(welcome("Arjun"));    // Hello, Arjun! Welcome to eStudy247.

Named + Default Export Together

// File: userHelper.js
export default function createUser(name) {
  return { name, createdAt: new Date().toISOString() };
}

export function validateEmail(email) {
  return email.includes("@");
}

export const MAX_USERS = 1000;
// File: app.js
import createUser, { validateEmail, MAX_USERS } from "./userHelper.js";

let user = createUser("Sneha");
console.log(user);

console.log(validateEmail("sneha@example.com")); // true
console.log(MAX_USERS);                           // 1000

Import Everything with * (Namespace Import)

All named exports from a module can be imported together under one namespace object.

import * as MathUtils from "./mathUtils.js";

console.log(MathUtils.add(4, 6));      // 10
console.log(MathUtils.subtract(9, 3)); // 6
console.log(MathUtils.PI);             // 3.14159

Re-exporting from a Module

A module can re-export items from another module. This is useful for creating a single entry point for a group of related modules.

// File: index.js (barrel file)
export { add, subtract } from "./mathUtils.js";
export { default as greet } from "./greet.js";
export { validateEmail } from "./userHelper.js";
// Now everything can be imported from one place:
import { add, greet, validateEmail } from "./index.js";

Dynamic Imports

Dynamic imports load a module only when needed — improving performance by not loading everything upfront. They return a Promise.

// Load a module only when a button is clicked
document.getElementById("loadBtn").addEventListener("click", async () => {
  const { add, subtract } = await import("./mathUtils.js");
  console.log(add(5, 3));  // 8
});

Dynamic imports are useful for:

  • Lazy loading large libraries only when needed
  • Loading modules based on user actions or conditions
  • Code splitting in large applications

Module Scope — Variables Are Private by Default

In a module, variables declared at the top level are scoped to that module. They are not added to the global scope.

// File: config.js
let apiKey = "abc123-secret";  // Private to this module

export function getKey() {
  return apiKey;  // Exposed only through this function
}
// File: app.js
import { getKey } from "./config.js";

console.log(getKey());    // abc123-secret
// console.log(apiKey);  // ReferenceError — apiKey is private to config.js

Module Characteristics

FeatureBehavior
Strict modeModules always run in strict mode automatically
ScopeTop-level variables are module-scoped, not global
Executed onceA module is loaded and run only once, even if imported multiple times
DeferredModule scripts are deferred — they run after the HTML is parsed
CORSModules loaded from external URLs require CORS headers

Named vs Default Export — When to Use Which

SituationUse
Exporting multiple utilities (functions, constants)Named exports
Exporting a single primary class or functionDefault export
Grouping related exports under one module fileNamed exports with barrel file

Practical Example — Product Module

File: products.js

const products = [
  { id: 1, name: "Notebook", price: 50 },
  { id: 2, name: "Pen",      price: 15 },
  { id: 3, name: "Ruler",    price: 25 }
];

export function getAllProducts() {
  return products;
}

export function getProductById(id) {
  return products.find(p => p.id === id);
}

export function getTotalValue() {
  return products.reduce((sum, p) => sum + p.price, 0);
}

export default products;

File: app.js

import products, { getAllProducts, getProductById, getTotalValue } from "./products.js";

console.log(getAllProducts());       // Full list
console.log(getProductById(2));      // { id: 2, name: "Pen", price: 15 }
console.log(getTotalValue());        // 90
console.log(products.length);       // 3 (default export)

Key Points to Remember

  • Modules split code into separate files, each with their own private scope
  • Use export to make values available outside a module
  • Use import to bring values from another module into the current file
  • Named exports can export multiple values; they must be imported using the same names
  • A module can have only one default export; it can be imported with any name
  • Use as to rename imports or exports
  • Dynamic imports (import()) load modules on demand and return a Promise
  • Add type="module" to the script tag in HTML to enable module support
  • Modules always run in strict mode automatically

Leave a Comment

Your email address will not be published. Required fields are marked *