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.14159Renaming 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)); // 5Default 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); // 1000Import 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.14159Re-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.jsModule Characteristics
| Feature | Behavior |
|---|---|
| Strict mode | Modules always run in strict mode automatically |
| Scope | Top-level variables are module-scoped, not global |
| Executed once | A module is loaded and run only once, even if imported multiple times |
| Deferred | Module scripts are deferred — they run after the HTML is parsed |
| CORS | Modules loaded from external URLs require CORS headers |
Named vs Default Export — When to Use Which
| Situation | Use |
|---|---|
| Exporting multiple utilities (functions, constants) | Named exports |
| Exporting a single primary class or function | Default export |
| Grouping related exports under one module file | Named 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
exportto make values available outside a module - Use
importto 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
asto 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
