Skip to content
Laviyx Learn

Try

JavaScript course menu

JavaScript Modules: import and export Explained

Learn JavaScript ES modules: named and default exports, importing with import, renaming, namespace imports, modules in the browser with type=module, modules in Node.js (.mjs and type: module), dynamic import(), module scope, and CommonJS require.

Advanced7 min readUpdated
On this page

As programs grow, keeping everything in one file becomes unmanageable. Modules let you split code into files, each with its own scope, that explicitly export what others may use and import what they need.

Exporting and importing#

math-utils.js (JavaScript)
export const GST_RATE = 0.18;

export function withGst(price) {
  return price + price * GST_RATE;
}

export function formatPrice(amount) {
  return `₹${amount.toFixed(2)}`;
}

function internalHelper() {
  // not exported: private to this file
}
main.js (JavaScript)
import { withGst, formatPrice } from "./math-utils.js";

console.log(formatPrice(withGst(500)));   // ₹590.00
  • export before a declaration makes it available to other modules.
  • import { a, b } from "./file.js" brings in exactly those names.
  • Anything not exported stays private to its file.
  • The path starts with ./ or ../ for your own files, and in browsers it must include the .js extension.

Default exports#

A module can have one default export, typically its main class or function. It is imported without braces, under any name you choose:

ShoppingCart.js (JavaScript)
export default class ShoppingCart {
  items = [];
  add(item) { this.items.push(item); }
}
main.js (JavaScript)
import ShoppingCart from "./ShoppingCart.js";
import Cart from "./ShoppingCart.js";          // any name works for a default export

const cart = new ShoppingCart();
Named exportDefault export
How many per fileAny numberAt most one
Exportexport function save() {}export default function save() {}
Importimport { save } from "./x.js"import save from "./x.js"
Name when importingMust match (or be renamed with as)Any name

Many teams prefer named exports, because the names stay consistent across the codebase and editors can auto-import them reliably.

Renaming and namespace imports#

JavaScript
import { formatPrice as format } from "./math-utils.js";   // rename on import
import * as MathUtils from "./math-utils.js";              // everything, as one object

console.log(format(10));
console.log(MathUtils.GST_RATE);

You can also export a list at the end of a file, and re-export from another module:

JavaScript
export { withGst, formatPrice as format };
export { default as ShoppingCart } from "./ShoppingCart.js";

Modules in the browser#

Load the entry file with type="module":

HTML
<script type="module" src="main.js"></script>

Module scripts:

  • can use import and export;
  • are deferred automatically, so they run after the HTML is parsed;
  • run in strict mode;
  • have their own scope: top-level variables are not globals;
  • must be served over HTTP. Opening the HTML file directly (file:///) blocks module imports, so use a local server such as VS Code's Live Server extension.

Modules in Node.js#

Node.js supports two module systems:

ES modules (modern)CommonJS (older)
Syntaximport / exportrequire() / module.exports
Enabled by.mjs files, or "type": "module" in package.json.cjs files, or the default without "type": "module"
Top-level awaitYesNo
CommonJS, in older code (JavaScript)
const fs = require("fs");
module.exports = { withGst };

New projects should use ES modules; you will still meet CommonJS in many existing packages and tutorials.

Built-in Node modules are imported with the node: prefix:

JavaScript
import { readFileSync } from "node:fs";
import path from "node:path";

console.log(path.extname("report.final.pdf"));
console.log(typeof readFileSync);
Output
.pdf
function

Dynamic import()#

import() used as a function loads a module on demand and returns a promise. It is used to load heavy code only when it is needed (code splitting):

JavaScript
const { default: path } = await import("node:path");
console.log(path.basename("/photos/2026/goa.jpg"));
Output
goa.jpg
JavaScript
// In a browser: load the chart library only when the user opens the chart
button.addEventListener("click", async () => {
  const { drawChart } = await import("./chart.js");
  drawChart(data);
});

How modules behave#

  • A module's code runs once, the first time it is imported. Every importer shares the same instance.
  • Imports are live bindings: if a module changes an exported let, importers see the new value. But importers cannot reassign an import.
  • Static import statements are hoisted and resolved before the module runs, which lets build tools remove unused code ("tree shaking").

Bundlers#

Large web apps often use a bundler or build tool (Vite, webpack, esbuild) that follows the imports, combines files, removes unused code and lets you import packages installed with npm by name, such as import { format } from "date-fns".

Practice#

Practice exercise

Split this code into two modules: validators.js, which exports isEmail and isStrongPassword as named exports, and main.js, which imports both and uses them. Then show how main.js would import everything from validators.js as one object instead.

JavaScript
function isEmail(s) { return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(s); }
function isStrongPassword(s) { return s.length >= 12; }
console.log(isEmail("asha@example.com"), isStrongPassword("short"));
Show solution
validators.js (JavaScript)
export function isEmail(s) {
  return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(s);
}

export function isStrongPassword(s) {
  return s.length >= 12;
}
main.js (JavaScript)
import { isEmail, isStrongPassword } from "./validators.js";

console.log(isEmail("asha@example.com"), isStrongPassword("short"));   // true false
main.js (namespace import) (JavaScript)
import * as validators from "./validators.js";

console.log(validators.isEmail("asha@example.com"));   // true

Key takeaway#

Finished this lesson?