Blog
Oct 12, 2025 - 11 MIN READ
Exploring Modules in JavaScript and TypeScript: import & export Explained

Exploring Modules in JavaScript and TypeScript: import & export Explained

Learn how JavaScript and TypeScript use modules to organize code with import/export — covering CommonJS vs ESM, default vs named exports, re-exports, and real project examples.

dogunfx

dogunfx

As your projects grow, splitting code into multiple files becomes essential. Modules make this possible by allowing you to organize logic and share functions, classes, or constants across files.

This post explores how JavaScript and TypeScript handle modules — including import, export, default vs named exports, and how the systems differ in Node.js, Bun, and browsers.


1) What Are Modules?

A module is simply a file that exports something for use in another file.

Without modules, all code shares one global scope (like old <script> tags). Modules isolate logic, making projects easier to maintain.

// greetings.js
export function sayHello(name) {
  return `Hello, ${name}!`;
}

// app.js
import { sayHello } from "./greetings.js";

console.log(sayHello("Dogunfx"));

Running this in Node (with ESM) or Bun prints:

Hello, Dogunfx!

2) Named Exports — Exporting Multiple Values

You can export several things from a file.

// math.js
export const PI = 3.14159;

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

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

Importing:

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

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

Aliasing on Import

import { add as plus } from "./math.js";
console.log(plus(2, 2)); // 4

3) Default Exports — One Main Thing per File

Sometimes you want a file to export one primary value.

// logger.js
export default function log(msg) {
  console.log("[LOG]", msg);
}

Importing:

import log from "./logger.js";

log("Modules are cool!");

You can combine default and named exports — though it’s best to keep one style per file for clarity.

export default function main() {}
export const version = "1.0.0";

4) Aggregating Exports (Re-exports)

In larger projects, you might re-export items to simplify imports.

// api/user.js
export function getUser(id) {
  return { id, name: "Ada" };
}

// api/post.js
export function getPosts() {
  return ["Post 1", "Post 2"];
}

// api/index.js
export * from "./user.js";
export * from "./post.js";

Now consumers can import from one place:

import { getUser, getPosts } from "./api/index.js";

5) Module Types: CommonJS vs ES Modules

CommonJS (CJS)

Used in older Node.js projects.

// old.js
const fs = require("fs");
module.exports = { read: fs.readFileSync };

ES Modules (ESM)

Modern standard for browsers, Node 14+, Bun, Deno, and TypeScript.

// modern.js
import fs from "fs";
export const read = fs.readFileSync;

Switching Between Them (Node.js)

In package.json:

{
  "type": "module"
}

Files without extensions must now be .js (ESM) or .cjs (CommonJS).


6) Modules in TypeScript

TypeScript adds types, but the import/export syntax is identical.

// math.ts
export function add(a: number, b: number): number {
  return a + b;
}

export default function square(x: number): number {
  return x * x;
}
// app.ts
import square, { add } from "./math";

console.log(add(2, 3)); // 5
console.log(square(4)); // 16

Compiler Settings (tsconfig.json)

To enable module features correctly:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "NodeNext",
    "outDir": "dist",
    "rootDir": "src"
  }
}

Bun and modern Node handle import syntax directly — no need to compile for simple projects.


7) Importing JSON, CSS, or Other Assets (Modern Runtimes)

Modern runtimes allow non-code imports.

JSON Import (ESM Only)

import config from "./config.json" assert { type: "json" };
console.log(config);

Dynamic Imports (Lazy Loading)

if (process.env.DEBUG) {
  const { debug } = await import("./debug.js");
  debug("Loaded dynamically!");
}

8) Real-World Example — Organizing a Project

project/
├── src/
│   ├── utils/
│   │   ├── math.js
│   │   └── logger.js
│   ├── api/
│   │   ├── users.js
│   │   ├── posts.js
│   │   └── index.js
│   └── app.js
└── package.json

src/utils/math.js

export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

src/utils/logger.js

export default (msg) => console.log("[App]", msg);

src/api/index.js

export * from "./users.js";
export * from "./posts.js";

src/app.js

import log from "./utils/logger.js";
import { add } from "./utils/math.js";
import { getUser } from "./api/index.js";

log("Starting app...");
console.log("Sum:", add(2, 3));
console.log("User:", getUser("u1"));

Run:

node src/app.js
# or
bun run src/app.js

9) Common Gotchas

IssueCauseFix
SyntaxError: Cannot use import statement outside a moduleNode defaulted to CommonJSAdd "type": "module" to package.json
Import path missing .js extensionNode ESM requires extensionsUse "./file.js" explicitly
Mixed import/export stylesUsing CJS + ESM togetherConvert to ESM or stick to one style
Circular importsTwo modules depend on each otherRestructure or use lazy/dynamic import

10) Summary

ConceptDescription
Named exportsExport multiple items per file
Default exportSingle main export per file
Re-exportCombine multiple modules
CommonJSLegacy Node.js system (require, module.exports)
ESMModern standard for JS/TS across browsers & runtimes

References


By mastering modules, you’ll gain control over how your project grows — avoiding tangled scripts and embracing structure. Whether in plain JS or TypeScript, import and export are your best tools for writing maintainable, scalable software.

Built by DOGUNFX using Nuxt UI • © 2025