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?
- npm resolution:
import React from "react"— browsers can't resolve bare names - Bundling/splitting: 500 modules → few optimized files (fewer round trips)
- Transforms: TypeScript, JSX, minification, dead-code elimination
- Dev servers: hot reload, proxies
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