Skip to main content
Madhukar
All Articles

JavaScript Modules: Import and Export Explained

July 29, 20265 min read
JavaScriptEs6ModulesSoftware Engineering
JavaScript Modules: Import and Export Explained

The problem before modules existed

Picture a JavaScript project where every file’s code is loaded into the same global space — no separation, no boundaries. A function named formatDate in one file could silently collide with a different formatDate accidentally defined somewhere else. Finding out which file actually defines a given function meant scrolling through files hoping to spot it, or hoping whoever wrote it left a comment. As a project grows past a handful of files, this stops being a minor annoyance and starts being a genuine source of bugs.

Modules exist to solve exactly this — giving each file its own private scope, and a deliberate, explicit way to share only what it chooses to.

Why Modules Are Needed

The code organization problem

Without modules, every script loaded onto a page effectively shares one giant global space. Two files that happen to declare a variable or function with the same name will silently conflict — whichever loads last simply overwrites the other, often without any error at all.

The dependency problem

Without an explicit system for declaring “this file needs that file,” developers had to carefully manage <script> tag order by hand — loading files in exactly the right sequence, hoping nothing dependent on something else loaded too early.

The maintainability problem

As a codebase grows, understanding “where does this function actually come from” becomes genuinely difficult without an explicit declaration tracing it back to its source file.

What modules fix

A module is simply a single file with its own private scope — nothing inside it is accessible from the outside unless it’s deliberately exported. This single change eliminates naming collisions, makes dependencies explicit and traceable, and turns “where did this come from” into a question with an immediate, provable answer: right there in the import statement.

Exporting Functions or Values

Exporting is how a module explicitly marks something as available to other files.

// mathUtils.js
export function add(a, b) {
return a + b;
}

export function multiply(a, b) {
return a * b;
}

export const PI = 3.14159;

Anything not explicitly exported — helper functions, internal variables — stays completely private to that file, invisible to anything importing from it. This is a deliberate design choice: a module’s internal implementation details stay hidden, and only its intended public surface is exposed.

Importing Modules

Importing brings exported values from one module into another, explicitly naming exactly what you want:

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

console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20
console.log(PI); // 3.14159

The path (./mathUtils.js) tells JavaScript exactly which file to pull these values from — making the dependency between app.js and mathUtils.js explicit and traceable, instead of implicit and easy to lose track of.

Renaming an import

If two modules happen to export something with the same name, you can rename it on import to avoid a collision:

import { add as sum } from "./mathUtils.js";
console.log(sum(2, 3)); // 5

Default vs Named Exports

JavaScript modules support two distinct export styles, and understanding the difference between them matters.

Named exports

A module can have multiple named exports — each imported using the exact same name it was exported with (unless explicitly renamed), wrapped in curly braces:

// mathUtils.js
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
import { add, subtract } from "./mathUtils.js";

Default exports

A module can also have one default export — the “main” thing that file is considered to provide, imported without curly braces, and under whatever name you choose:

// formatDate.js
export default function formatDate(date) {
return date.toISOString().split("T")[0];
}
import formatDate from "./formatDate.js"; // name here is your choice
import myOwnName from "./formatDate.js"; // works identically — default exports aren't tied to a specific name

Combining both in one file

// userUtils.js
export default function getUser(id) { /* ... */ }
export function validateEmail(email) { /* ... */ }
import getUser, { validateEmail } from "./userUtils.js";

When to use which

A default export fits a file that has one clear, primary purpose — a single component, a single main function. Named exports fit a file exporting several related, equally important utilities, like a shared mathUtils.js full of small helper functions.

Benefits of Modular Code

No naming collisions

Since each module has its own private scope, two files can each define their own internal formatDate function without ever conflicting — only what's explicitly exported is visible outside.

Clear, explicit dependencies

An import statement at the top of a file is a complete, honest list of exactly what that file depends on — no guessing, no needing to trace through global scope to figure out where something came from.

Easier maintainability

Because a module’s private internals stay hidden, you can freely refactor a function’s implementation without breaking anything elsewhere — as long as its exported behavior (what it’s called, what it returns) stays the same.

Better reusability

A well-scoped module (like a mathUtils.js full of small, focused functions) can be imported into any file, or even any project, that needs it — without dragging along unrelated code it doesn't actually need.

A clearer mental map of the codebase

A project built from small, focused modules — each with clear exports — naturally forms a file dependency graph: which files rely on which others becomes visible just from reading import statements across the codebase, rather than needing to hold the entire project's structure in your head at once.

Final Takeaway

Modules solve a problem that’s easy to underestimate until a codebase actually grows large enough to feel it: without them, every file shares one global space, and nothing declares what depends on what. export lets a file deliberately choose what it shares; import lets another file explicitly declare exactly what it needs, and from where. Named exports suit a file full of related utilities; a default export suits a file built around one clear, primary purpose. None of this is complicated once it clicks — it's simply JavaScript giving every file the same thing a well-organized project needs everywhere else: clear boundaries, and an honest record of what depends on what.

Frequently Asked Questions

Can a file have both a default export and named exports at the same time?

> Yes — a single module can export one default value alongside any number of named exports, and both can be imported together in the same import statement, as shown in the combined example above.

Does the name used when importing a default export have to match anything?

> No — a default export isn’t tied to a specific name; whoever imports it chooses whatever name makes sense in their own file. Named exports, by contrast, must be imported using their exact original name, unless explicitly renamed with as.

What’s the difference between ES module syntax (import/export) and CommonJS (require/module.exports)?

> They’re two different module systems — ES modules (import/export) are the modern, standard JavaScript approach used in browsers and current Node.js projects; CommonJS (require/module.exports) is the older system still found in many existing Node.js codebases. This article focuses on ES module syntax, which is the current standard going forward.

Do I need a bundler to use JavaScript modules?

> Not necessarily — modern browsers and Node.js both support import/export natively. Bundlers become relevant for combining many modules into optimized production files, but that's a separate, later concern from understanding how modules themselves work.

Originally published by Mr Madhukar

Read the complete article on Medium with full formatting & reader responses.