Exporting and Importing Modules
Introductionβ
As your JavaScript projects grow, you'll quickly realize that putting all code in one file becomes a nightmare! Imagine a 10,000-line fileβimpossible to navigate and maintain! π΅
This is where modules come to the rescue. Modules let you split your code into separate files that can share functionality with each other.
The Problem: Everything in One File πβ
// app.js (10,000 lines of chaos!)
function calculateTax(price) { /* ... */ }
function validateEmail(email) { /* ... */ }
function formatDate(date) { /* ... */ }
function connectDatabase() { /* ... */ }
// ... 9,950 more lines ...
Problems:
- Hard to find specific code
- Multiple developers can't work simultaneously
- Can't reuse code in other projects
- Testing is difficult
The Solution: Modules! β¨β
// utils/tax.js
export function calculateTax(price) { /* ... */ }
// utils/validation.js
export function validateEmail(email) { /* ... */ }
// utils/formatting.js
export function formatDate(date) { /* ... */ }
// database/connection.js
export function connectDatabase() { /* ... */ }
// app.js (clean and organized!)
import { calculateTax } from './utils/tax.js';
import { validateEmail } from './utils/validation.js';
import { formatDate } from './utils/formatting.js';
import { connectDatabase } from './database/connection.js';
Benefits:
- β Organized - Each file has a clear purpose
- β Reusable - Import code into any project
- β Maintainable - Easy to find and fix bugs
- β Collaborative - Multiple developers can work on different files
- β Testable - Test each module independently
Two Module Systems πβ
JavaScript has two main module systems:
-
ES6 Modules (Modern, recommended)
- Uses
importandexport - Browser and Node.js (newer versions)
- The standard for modern JavaScript
- Uses
-
CommonJS (Older, Node.js)
- Uses
require()andmodule.exports - Traditional Node.js modules
- Still widely used in Node.js projects
- Uses
Start with ES6 Modules (import/export) - they're the modern standard and work everywhere (browsers + Node.js). Learn CommonJS later if you work with older Node.js projects.
Let's dive into both systems!
ES6 Modules (Modern Way) πβ
ES6 introduced a clean, modern syntax for working with modules. This is the recommended approach for new projects!
The Two Types of Exportsβ
Think of exports like different ways to share items from a toolbox:
- Default Export - The "main tool" in the box (one per file)
- Named Exports - Multiple specific tools you can pick from
Toolbox (module.js)
βββ π§ Main Tool (default export) β Only one
βββ π¨πͺβοΈ Other Tools (named exports) β Many
Quick Decision Guide:
// One main thing to export? Use default export
export default Calculator;
// Multiple things to export? Use named exports
export { add, subtract, multiply, divide };
// Can also mix both!
export default Calculator;
export { PI, E };
Default Export (One Main Export) π¦β
Use default export when a file has one main thing to share.
When to use:
- A file focused on one component/class/function
- The main feature of the module
- Want flexibility in import naming
// Option 1: Export at declaration
export default function calculate(a, b, operation) {
if (operation === 'add') return a + b;
if (operation === 'subtract') return a - b;
return 0;
}
// Option 2: Export at the end
function calculate(a, b, operation) {
if (operation === 'add') return a + b;
if (operation === 'subtract') return a - b;
return 0;
}
export default calculate;
// Can name it whatever you want!
import calculator from "./calculator.js";
// OR
import calc from "./calculator.js";
// OR
import anything from "./calculator.js";
// All work the same way
console.log(calculator(5, 3, 'add')); // 8
With default exports, you can name the import whatever you want because there's only one thing being exported:
// utils.js
export default function greet() {
return "Hello!";
}
// app.js - ALL of these work!
import greet from "./utils.js"; // β
import sayHello from "./utils.js"; // β
import greeting from "./utils.js"; // β
import xyz from "./utils.js"; // β
(works but confusing!)
Best practice: Use meaningful names that describe what you're importing!
Real-World Examplesβ
Example 1: Exporting a Class
export default class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
import User from "./User.js";
const user = new User("Alice", "alice@example.com");
console.log(user.greet()); // "Hello, I'm Alice"
Example 2: Exporting Configuration
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
};
export default config;
import config from "./config.js";
console.log(config.apiUrl); // "https://api.example.com"
Named Exports (Multiple Exports) πβ
Use named exports when you want to share multiple things from one file.
When to use:
- A file with multiple utility functions
- Sharing constants and functions together
- Want precise control over what gets imported
// Option 1: Export individually
export const PI = 3.14159;
export const E = 2.71828;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// Option 2: Export all at once (cleaner!)
const PI = 3.14159;
const E = 2.71828;
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
export { PI, E, add, subtract };
// Import specific things (use exact names!)
import { PI, add, subtract } from "./mathUtils.js";
console.log(PI); // 3.14159
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
// E is NOT imported, so this would error:
// console.log(E); // β Error: E is not defined
Unlike default exports, named exports must be imported with their exact names:
// utils.js
export const name = "Alice";
export const age = 25;
// app.js
import { name, age } from "./utils.js"; // β
Correct
import { userName, age } from "./utils.js"; // β Error! No 'userName' export
Renaming Imports/Exports with asβ
Sometimes you need different names to avoid conflicts:
Rename When Importing:
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// Rename during import
import { add as sum, subtract as minus } from "./mathUtils.js";
console.log(sum(5, 3)); // 8 (renamed from 'add')
console.log(minus(5, 3)); // 2 (renamed from 'subtract')
Rename When Exporting:
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
// Rename during export
export { add as sum, subtract as minus };
// Must use the exported names
import { sum, minus } from "./mathUtils.js";
console.log(sum(5, 3)); // 8
console.log(minus(5, 3)); // 2
Real-World Example: Utility Functionsβ
export function isEmail(str) {
return str.includes("@");
}
export function isPhoneNumber(str) {
return /^\d{10}$/.test(str);
}
export function isZipCode(str) {
return /^\d{5}$/.test(str);
}
export const MIN_PASSWORD_LENGTH = 8;
export const MAX_USERNAME_LENGTH = 20;
import {
isEmail,
isPhoneNumber,
MIN_PASSWORD_LENGTH
} from "./validators.js";
console.log(isEmail("user@example.com")); // true
console.log(isPhoneNumber("1234567890")); // true
console.log(MIN_PASSWORD_LENGTH); // 8
Importing Everything with *β
You can import all named exports at once:
export const PI = 3.14159;
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// Import everything into a namespace
import * as math from "./mathUtils.js";
console.log(math.PI); // 3.14159
console.log(math.add(5, 3)); // 8
console.log(math.subtract(5, 3)); // 2
Use import * when:
- You need many exports from one file
- You want a clear namespace (e.g.,
math.add,utils.format) - You want to avoid naming conflicts
CommonJS Export and Importβ
CommonJS is an older module system. It uses a different syntax for Import and Export statements compared to ES6.
module.exports and requireβ
Check out the following example to understand how to export and import a module in CommonJS.
const message = "Hello World";
module.exports = message;
The module.exports object is used to export the module. It can be used to export a single value or multiple values from a module. In the above example, we have exported a single value, message, from the module.js file.
The require statement is used to import the exported module from another file. The syntax for importing a module in CommonJS is as follows:
const message = require("./module.js");
console.log(message); // Hello World
In the above example, the module.js file exports a single value, message, using the module.exports object. The import.js file then imports this value using the require statement. The require statement returns the value that was exported from the module.js file. This is kind of a default export in CommonJS.
module.exportsThe module.exports object can be used to export multiple values from a module. The syntax for exporting multiple values from a module is as follows:
const name = "John";
const age = 30;
function add(a, b) {
return a + b;
}
module.exports = {
name,
age,
add,
};
exports and requireβ
The exports object is used to export multiple values from a module. The syntax for exporting multiple values from a module is as follows:
const name = "John";
const age = 30;
function add(a, b) {
return a + b;
}
exports.name = name;
exports.age = age;
exports.add = add;
The require statement is used to import the exported module from another file. The syntax for importing a module in CommonJS is as follows:
const { name, age, add } = require("./module.js");
console.log(name, age); // John 30
console.log(add(2, 3)); // 5
In the above example, the module.js file exports multiple values, name, age, and add, using the exports object. The import.js file then imports these values using the require statement. The require statement returns an object that contains the exported values. We can then destructure the object to get the values.
Default vs Named Exports: When to Use Which? π€β
| Aspect | Default Export | Named Exports |
|---|---|---|
| How many? | One per file | Multiple per file |
| Import name | Any name you want | Must match export name |
| Syntax | export default X | export { X, Y, Z } |
| Best for | Main component/class | Utilities, constants |
| Example | export default User | export { add, subtract } |
Practical Guidelinesβ
Use Default Export when:
// β
File has one main purpose
// Button.js
export default function Button() { /* ... */ }
// User.js
export default class User { /* ... */ }
// config.js
export default { apiUrl: "...", timeout: 5000 };
Use Named Exports when:
// β
File has multiple related utilities
// mathUtils.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
// validators.js
export function isEmail(str) { /* ... */ }
export function isPhone(str) { /* ... */ }
export function isZip(str) { /* ... */ }
Mixing Both (Advanced)β
You can use both default and named exports in the same file:
// Default export - main feature
export default class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
// Named exports - extras
export const PI = 3.14159;
export const E = 2.71828;
export function round(num) {
return Math.round(num);
}
// Import both
import Calculator, { PI, round } from "./calculator.js";
const calc = new Calculator();
console.log(calc.add(5, 3)); // 8
console.log(PI); // 3.14159
console.log(round(3.7)); // 4
Common Mistakes to Avoid π¨β
Mistake 1: Missing File Extensionβ
// β Wrong (in browsers)
import { add } from "./utils";
// β
Right
import { add } from "./utils.js";
Mistake 2: Confusing Default and Namedβ
// utils.js
export const add = (a, b) => a + b; // Named export
// β Wrong (trying to import named as default)
import add from "./utils.js";
// β
Right
import { add } from "./utils.js";
Mistake 3: Importing Non-Existent Exportsβ
// utils.js
export const add = (a, b) => a + b;
// β Wrong (subtract doesn't exist)
import { add, subtract } from "./utils.js"; // Error!
// β
Right
import { add } from "./utils.js";
Mistake 4: Modifying Importsβ
import { user } from "./data.js";
// β Wrong (imports are read-only)
user = { name: "Bob" }; // Error!
// β
Right (if object, can modify properties)
user.name = "Bob"; // OK
Quick Reference Cheat Sheet πβ
// ββββββββββ EXPORTING ββββββββββ
// Default export (one per file)
export default function() { }
export default class User { }
export default { key: "value" }
// Named exports (multiple per file)
export const PI = 3.14;
export function add() { }
export class User { }
// Export multiple at once
const a = 1, b = 2;
export { a, b };
// Export with rename
export { a as alpha, b as beta };
// ββββββββββ IMPORTING ββββββββββ
// Default import
import MyName from "./file.js"
// Named imports
import { name, age } from "./file.js"
// Import with rename
import { name as userName } from "./file.js"
// Import all as namespace
import * as Utils from "./file.js"
// Use as: Utils.name, Utils.age
// Mix default + named
import DefaultThing, { namedThing } from "./file.js"
Conclusionβ
Modules are essential for organizing modern JavaScript applications! We learned:
β
ES6 Modules - Modern import/export syntax
β
Default Exports - One main export per file
β
Named Exports - Multiple specific exports
β
Import Renaming - Use as to rename
β
CommonJS - Older require/module.exports (Node.js)
β
Best Practices - When to use each pattern
Remember: Start with ES6 modules for new projects. They're the modern standard and work everywhere! π