Overview
Vite's founding insight is that development and production are different problems and deserve different tools. In dev it doesn't bundle at all: source modules are served to the browser as native ESM, transformed on demand, with dependencies pre-bundled once by esbuild — so cold start is near-instant and HMR cost scales with the edit, not the app. For production it hands the whole graph to Rollup (now Rolldown) for the optimized, split, minified output the dev model can't provide. That split, plus a Rollup-compatible plugin API, made Vite the shared substrate almost every modern frontend framework builds on — the closest thing the frontend has to a default build tool.
Key points
- The dev server serves, it doesn't bundle: each source file is transformed
on request and cached; an edit invalidates one module and HMR swaps it. Bare imports
from
node_modulesare pre-bundled with esbuild once (CJS → ESM, hundred-file packages collapsed to one) so the browser isn't hit with request waterfalls. index.htmlis the entry: not a JS file — Vite crawls the module graph from the<script type="module">tag, and the HTML is a first-class part of the build.- Production builds through Rollup / Rolldown: full-graph tree-shaking, route-level code splitting, CSS extraction, hashed filenames. Dev/prod parity is kept by sharing plugins and resolution logic across both modes.
- The plugin system is Rollup's, extended: a Vite plugin is a Rollup plugin with
extra hooks (
configureServer,transformIndexHtml,handleHotUpdate) and anapply: 'serve' | 'build'switch — one plugin ecosystem covers both halves. - Config and env:
vite.config.tswithdefineConfig;import.meta.envexposes mode and.envvariables (onlyVITE_-prefixed ones reach client code);definedoes compile-time constant replacement. - The framework substrate: SvelteKit, Astro, Nuxt, React Router (the Remix line), SolidStart and others are Vite plugins-plus-conventions rather than bespoke toolchains — one transform pipeline shared across the ecosystem.
- The Environment API (v6+) generalizes the dev server beyond the browser: multiple environments (client, SSR, edge workers) with their own module graphs and resolution, in one server — what frameworks needed to stop maintaining forked dev machinery.
- Vitest is the test-runner sibling: it reuses Vite's config, plugins and transform pipeline, so tests import exactly what the app builds — no parallel Babel/Jest transform universe to keep in sync.
- Library mode (
build.lib) turns Vite around: entry + formats out, dependencies externalized — though pure library work often reaches for tsup/tsdown instead. - The Rolldown arc: the VoidZero company (Vite's creator) is rebuilding the core in
Rust — Rolldown replaces both esbuild's and Rollup's roles, one engine for pre-bundle
and build;
rolldown-viteis production-ready and merging into Vite as the default.
The two modes side by side:
flowchart LR
subgraph dev["vite (dev)"]
SRC["source modules"] -->|"transformed on demand, served as native ESM"| BROWSER["browser"]
DEPS["node_modules deps"] -->|"esbuild pre-bundle, cached once"| BROWSER
end
subgraph prod["vite build"]
SRC2["whole module graph"] --> RB["Rollup / Rolldown"]
RB --> OUT["split, hashed, minified bundles + index.html"]
end
Details
Everyday commands
| Command | Effect |
|---|---|
vite |
Start the dev server (default port 5173) with HMR |
vite build |
Production build to dist/ |
vite preview |
Serve the built dist/ locally — a sanity check, not a prod server |
vitest |
Run tests through the same config and transform pipeline |
Where Vite is not the answer
Next.js keeps its own vertically-integrated toolchain (swc + Turbopack); pure backend
services don't need a dev server at all (see
backend builds); and a library that is
just "TS in, ESM + .d.ts out" is simpler on tsup/tsdown than on library mode.
Everywhere else in frontend land, Vite is the default that needs a reason not to be
chosen.
Examples
A minimal but realistic vite.config.ts:
import { defineConfig } from 'vite';
export default defineConfig({
server: {
port: 5173,
proxy: { '/api': 'http://localhost:8080' }, // dev-only backend proxy
},
build: {
sourcemap: true,
outDir: 'dist',
},
});
Framework plugins slot into a plugins: [] array; everything else is convention.
Related
- The TypeScript build ecosystem — parent summary: where Vite sits in the layered stack.
- The bundler world — the pieces Vite assembles: esbuild, Rollup, and the Rolldown convergence.
- Rendering strategies & meta-frameworks — the frameworks built on Vite and the rendering trade-offs they package.
- Frontend testing — Vitest in its testing context, beyond the shared-pipeline mechanics.