Skip to main content
๐Ÿ“ฆintermediate

Vite

Vite is a next-generation build tool that uses native ESM for blazing fast development and Rollup for optimized production builds. It's the modern default for React, Vue, and Svelte projects.

Why Vite?

Traditional bundlers like Webpack bundle your entire app before the dev server starts. As apps grow, this gets slow.

Vite's insight: modern browsers natively support ES modules. In development, serve files directly as ESM โ€” no bundling needed! The browser handles imports itself, and the dev server only processes what the browser requests.

Traditional (Webpack dev mode): Start โ†’ Bundle everything โ†’ Dev server ready (30s+ on large apps) Edit file โ†’ Re-bundle affected modules โ†’ Update (1-5s) Vite: Start โ†’ Dev server ready instantly (< 1s) Edit file โ†’ Only transform that one file โ†’ HMR update (< 100ms)

Getting Started

hljs bash
npm create vite@latest my-app -- --template react
# or: react-ts, vue, vue-ts, svelte, vanilla, etc.

cd my-app
npm install
npm run dev

Default project structure:

my-app/ index.html โ† Vite entry point (in root, not public/) public/ โ† static assets (served as-is) src/ main.jsx โ† JavaScript entry App.jsx App.css vite.config.js package.json

How Vite Works in Development

  1. You start vite dev server
  2. Browser requests index.html
  3. Vite serves index.html with a <script type="module"> referencing your entry
  4. Browser sees import App from './App.jsx' โ€” requests /src/App.jsx
  5. Vite transforms the file on request (JSX โ†’ JS, TypeScript โ†’ JS)
  6. Browser fetches dependencies recursively

Only imported modules are processed. Unused code is never touched.

Pre-bundling (esbuild): Vite bundles node_modules upfront with esbuild (written in Go, 10-100x faster than webpack). This converts CJS packages to ESM and bundles packages with many internal imports into one.

vite.config.js

hljs javascript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],

  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },

  server: {
    port: 3000,
    open: true,
    proxy: {
      "/api": {
        target: "http://localhost:8080",
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/api/, ""),
      },
    },
  },

  build: {
    outDir: "dist",
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ["react", "react-dom"], // separate vendor chunk
        },
      },
    },
  },

  css: {
    modules: {
      localsConvention: "camelCase", // CSS Module class names
    },
  },
});

Hot Module Replacement

Vite's HMR is fast and precise โ€” only the changed module and its direct importers are updated, without losing application state:

hljs javascript
// src/counter.js
export let count = 0;
export function increment() { count++; }

// Vite HMR API
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // Handle update
  });
}

With React plugin, HMR is handled automatically via React Refresh โ€” components update in-place without losing state.

Environment Variables

hljs bash
# .env
VITE_API_URL=http://localhost:8080
VITE_APP_NAME=MyApp
hljs javascript
// Access in JS (must be prefixed with VITE_)
console.log(import.meta.env.VITE_API_URL);
console.log(import.meta.env.MODE);      // "development" or "production"
console.log(import.meta.env.DEV);       // true in development
console.log(import.meta.env.PROD);      // true in production

Only VITE_-prefixed variables are exposed to client code (unlike Next.js which uses NEXT_PUBLIC_).

Static Assets

hljs javascript
// Import as URL (returns a string path)
import logo from "./logo.svg";
console.log(logo); // "/assets/logo.a3b2c1.svg"

// Import as raw string
import shaderCode from "./shader.glsl?raw";

// Import as URL explicitly
import workerUrl from "./heavy-work.js?url";

// Import as web worker
import MyWorker from "./worker.js?worker";
const worker = new MyWorker();

Vite vs Webpack: Side by Side

FeatureViteWebpack 5
Dev server startup< 1s10-60s
HMR< 100ms1-5s
Config complexitySimpleComplex
EcosystemGrowingMassive
Production bundlerRollupWebpack
CJS supportVia pre-bundlingNative
Framework supportPlugins for React/Vue/SvelteLoaders

Production Build

For production, Vite uses Rollup (not the dev server approach):

hljs bash
npm run build   # outputs to dist/
npm run preview # preview the production build locally

The production output is a fully bundled, tree-shaken, minified set of files โ€” similar to what Webpack produces. Both development speed and production optimization.

Vite Plugins

The plugin ecosystem is growing:

hljs javascript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";           // SVG as React components
import { VitePWA } from "vite-plugin-pwa";     // PWA support
import tsconfigPaths from "vite-tsconfig-paths"; // TypeScript paths

export default defineConfig({
  plugins: [
    react(),
    svgr(),
    tsconfigPaths(),
    VitePWA({ registerType: "autoUpdate" }),
  ],
});
โ–ถTry it yourself

Key Takeaways

  • Vite serves files directly as ESM in development โ€” no bundling, near-instant startup
  • Pre-bundles node_modules with esbuild (very fast) to convert CJS โ†’ ESM
  • Production builds use Rollup โ€” optimized tree-shaking and output
  • HMR updates are precise and don't reset component state
  • Environment variables must be prefixed with VITE_ to be exposed
  • vite.config.js is simpler than webpack configs, but still fully customizable

Ready to test your knowledge?

Take a quiz on what you just learned.

Take the Quiz โ†’