Bundlers and Vite

Once a project grows past a single file, you need a way to combine all your modules, styles, and assets into something a browser can load efficiently. That is the job of a bundler. A bundler takes your source files, resolves the dependencies between them, and produces optimized output for production.

Why We Need Bundlers

Browsers support ES modules natively, so you might ask why we do not just ship the source files as they are. The problem is that a real app has dozens or hundreds of modules, and each import can trigger another HTTP request. HTTP/2 and HTTP/3 help, but a large module graph still adds overhead.

Bundlers solve several problems:

  1. Module resolution: they combine many files into fewer bundles, reducing network requests
  2. Performance: they minify code and remove unused exports to shrink file sizes
  3. Transformation: they convert TypeScript, JSX, and modern syntax into browser-compatible code
  4. Optimization: they apply techniques like tree shaking and code splitting automatically

Getting Started with Vite

Vite (French for “fast”) is a modern build tool that uses native ES modules during development and a Rollup-compatible production pipeline (Rollup today, with Rolldown rolling out in newer releases). It is fast, easy to configure, and supports TypeScript, JSX, and CSS out of the box.

To scaffold a new project:

npm create vite@latest my-app
cd my-app
npm install
npm run dev

This gives you a dev server with Hot Module Replacement (HMR), meaning your changes appear instantly in the browser without a full page reload.

Understanding Vite’s Project Structure

After scaffolding, your project looks like this:

my-app/
├── index.html
├── package.json
├── vite.config.js
├── src/
│   ├── main.js
│   └── style.css
└── public/
    └── favicon.ico

Notice that index.html lives at the project root, not inside a public/ or dist/ folder. Vite treats it as the entry point and injects your scripts automatically.

Configuring Vite

Most projects need little or no configuration. But when you do need to customize things, Vite uses a vite.config.js file:

// vite.config.js
import { defineConfig } from "vite";

export default defineConfig({
  root: "./",
  base: "/",
  build: {
    outDir: "dist",
    minify: true,  // use Vite's default minifier for your installed version
    sourcemap: true,
  },
  server: {
    port: 3000,
    open: true,  // opens browser automatically
  },
});

The defineConfig helper gives you autocompletion in your editor, which helps you get the option names right.

Running Vite Commands

Vite projects come with three scripts in package.json:

npm run dev      # Start dev server with hot reload
npm run build    # Build optimized output for production
npm run preview  # Preview the production build locally

My advice is to always run npm run preview after building to verify that your production bundle works correctly before deploying.

Why Vite Over Other Tools

Vite has become the default choice for new projects, for several reasons:

  • Fast dev server — it serves source files as native ES modules and avoids full-app rebundles during development. Startup is usually near-instant.
  • Hot Module Replacement — changes show up in the browser within milliseconds.
  • Built-in support — TypeScript, JSX, CSS modules, and JSON imports all work without additional configuration.
  • Optimized production builds — it produces efficient, tree-shaken, code-split bundles out of the box.