📦 JavaScript

ES Modules in the Browser — And What Bundlers Actually Do

📅 Jul 4, 2026 ⏱ 3 min read

Modules natively, no build step — then a clear answer to "so why does Vite exist?"

Native modules

<script type="module" src="main.js"></script>
<!-- type=module gives you: imports, deferred by default, strict mode, own scope -->
// utils.js
export const fmt = (n) => n.toLocaleString("en-IN");
export default class Store { ... }

// main.js
import Store, { fmt } from "./utils.js";   // "./" required in browsers!

// dynamic import — load on demand
btn.onclick = async () => {
  const { renderChart } = await import("./charts.js");   // fetched only now
  renderChart(data);
};

Module scope beats script soup

Classic scripts share one global namespace — name collisions everywhere. Each module has its own scope; only exports are visible. This alone modernizes your vanilla projects.

So what do bundlers add?

Vite = instant dev server using native modules + Rollup for production builds — the current default. For learning projects, native modules alone are genuinely enough.

← All Articles