Skip to main content

Command Palette

Search for a command to run...

JavaScript Modules: Import and Export Explained

Updated
7 min readView as Markdown
T

MERN Stack developer focused on building real-world web applications. Currently exploring Generative AI and modern backend practices. I write about what I learn, build, and break along the way.

As your JavaScript code grows, keeping everything in one file stops working. Things get hard to find, hard to fix, and hard to reuse. Modules are how JavaScript lets you split your code into separate files and connect them cleanly.

The Problem - One File, Everything Crammed In

Imagine you're building a game. You start with one file:

// game.js

const playerName = "Arjun";
let playerHealth = 100;
let playerLevel = 1;

function greetPlayer() {
  console.log(`Welcome, ${playerName}!`);
}

function levelUp() {
  playerLevel++;
  console.log(`\({playerName} is now level \){playerLevel}`);
}

function takeDamage(amount) {
  playerHealth -= amount;
  console.log(`Health: ${playerHealth}`);
}

const enemyName = "Zombie";
let enemyHealth = 50;

function attackEnemy() {
  enemyHealth -= 20;
  console.log(`\({enemyName} health: \){enemyHealth}`);
}

// ...more code keeps getting added

This is fine at first. But two weeks later, this file is 600 lines. Player logic, enemy logic, UI logic, game logic all mixed together.

Now try to find a bug. Now try to reuse just the player logic in another project. Now try to work on this with a teammate without constantly overwriting each other's code.

The single-file approach breaks down fast. You need a way to split code into focused, independent pieces. That's what modules are for.

What a Module Is

A module is just a JavaScript file. Nothing special about the file itself what makes it a module is that it explicitly exports what it wants to share, and other files explicitly import what they need.

Everything else inside the file stays private. By default, nothing leaks out.

Exporting - Sharing What You Want

Named Exports

You can export multiple things from a file by putting export in front of them:

// player.js

export const playerName = "Arjun";
export let playerHealth = 100;

export function greetPlayer() {
  console.log(`Welcome, ${playerName}!`);
}

export function levelUp() {
  console.log(`${playerName} levelled up!`);
}

Or export everything at the bottom in one go:

// player.js

const playerName = "Arjun";
let playerHealth = 100;

function greetPlayer() {
  console.log(`Welcome, ${playerName}!`);
}

function levelUp() {
  console.log(`${playerName} levelled up!`);
}

export { playerName, playerHealth, greetPlayer, levelUp };

Both approaches do the same thing. The bottom export style is common because it makes it easy to see everything a file exports in one place.

Default Export

Every module can also have one default export the main thing that file is about:

// player.js

function greetPlayer(name) {
  console.log(`Welcome, ${name}!`);
}

export default greetPlayer;

A file can only have one default export. Think of it as the primary thing this module offers.

Importing - Using What You Need

Importing Named Exports

When importing named exports, you use the exact same name wrapped in curly braces:

// game.js

import { playerName, greetPlayer, levelUp } from "./player.js";

greetPlayer();   // Welcome, Arjun!
levelUp();       // Arjun levelled up!

You only import what you need. If a file exports ten things and you only need two, you only bring in two.

Renaming on Import

If a name conflicts with something already in your file, rename it during import:

import { levelUp as playerLevelUp } from "./player.js";

playerLevelUp();

Importing Default Exports

Default exports don't use curly braces, and you can name them anything you want:

// game.js

import greetPlayer from "./player.js";

// or call it whatever you like
import welcome from "./player.js";

welcome("Arjun"); // Welcome, Arjun!

Since there's only one default export per file, JavaScript knows what you're referring to regardless of what you name it on import.

Importing Everything at Once

If you need everything a module exports, use * with an alias:

import * as Player from "./player.js";

Player.greetPlayer();
Player.levelUp();
console.log(Player.playerName);

Everything lives under the Player namespace. Useful when you're using many exports from the same file.

Default vs Named Exports - When to Use Which

This is where people get confused. Here's the practical difference:

Named exports are for files that offer multiple things utility functions, constants, helper methods. The caller picks what they need.

// utils.js
export function formatDate(date) { ... }
export function capitalize(str) { ... }
export function slugify(str) { ... }

Default export is for files that are primarily about one thing a class, a component, a main function.

// PlayerCard.js
export default function PlayerCard({ name, level }) { ... }
Named Export Default Export
Syntax (export) export const x = ... export default x
Syntax (import) import { x } from ... import x from ...
How many per file As many as you want Only one
Name on import Must match (or rename with as) Anything you want
Best for Multiple utilities One main thing

You can also mix both in the same file — one default export and several named exports together.

How This Solves the Original Problem

Going back to the messy game.js with modules, you split it up:

game/
├── player.js    → player logic only
├── enemy.js     → enemy logic only
├── ui.js        → display logic only
└── game.js      → pulls everything together
// player.js
export const playerName = "Arjun";
export function greetPlayer() { ... }
export function levelUp() { ... }
// enemy.js
export const enemyName = "Zombie";
export function attackEnemy() { ... }
// game.js
import { greetPlayer, levelUp } from "./player.js";
import { attackEnemy } from "./enemy.js";

greetPlayer();
attackEnemy();
levelUp();

game.js is now just the orchestrator. Each file has one job. Each file is short, readable, and independently maintainable.

Benefits of Modular Code

Separation of concerns — player logic lives in player.js, enemy logic lives in enemy.js. Each file has one clear purpose.

Reusability — need the same utility function in another project? Copy that one file, not the entire codebase.

Easier debugging — when something breaks, you know which file to look at. A bug in player health narrows it down to player.js immediately.

Team-friendly — two developers can work on player.js and enemy.js simultaneously without touching the same file.

Private by default — anything you don't export stays inside the file. No accidental variable collisions between files.

One Thing to Keep in Mind

Modules use type="module" in the browser when linking your script:

<script type="module" src="game.js"></script>

Without this, import and export won't work in the browser. In Node.js, you either use .mjs file extensions or add "type": "module" to your package.json.

Bundlers like Vite or Webpack handle this automatically in modern projects — but it's good to know why it's needed.

Final Thought

Modules don't teach you new JavaScript. They teach you how to organise the JavaScript you already know.

A codebase where every function, variable, and logic lives in one file is not a small project with one file it's a large project waiting to become unmaintainable. Splitting code into modules is one of the most impactful habits you can build early, because the cost of not doing it only grows as your project does.

Export what's needed. Import what's used. Keep everything else private. That's it.

JavaScript for Builders - The Tejas Dev Series

Part 4 of 25

JavaScript is easy to start but hard to truly understand. Most developers learn syntax. Few understand how it actually works. This series is my structured journey into mastering JavaScript fundamentals — explained with clarity, visual mental models, and real debugging insights. No shortcuts. No surface-level tutorials. We break down: How JavaScript executes code Scope, memory, and references Execution context & call stack Shallow vs deep copy ES6+ features that actually matter Common beginner traps Each article follows a clear structure: Confusion → Deep Breakdown → Visual Explanation → Real Use Case → Clear Takeaway. If you're a self-taught or bootcamp developer who wants clarity instead of copy-paste coding this series is built for you. Welcome to JavaScript for Builders. — Tejas Dev

Up next

Destructuring in JavaScript

Imagine you just looted an enemy in BGMI. Inside the loot box, you see • Medkit• Ammo• Grenade• Scope Now you don’t pick the entire loot box… You pick only what you need. Maybe just ammo and medkit. T