Build Tool

Vite

Vite is the modern build tool that serves your source over native ES modules in development and bundles it with Rollup for production. Fast to start, fast to iterate.

intermediate14 min readUpdated Sep 15, 2026
vite.config.ts
ts
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: { port: 5173, open: true },
  build: {
    outDir: "dist",
    sourcemap: true,
  },
});
Created by
Evan You, 2020
Dev server
Native ES modules
Production build
Rollup
Config
vite.config.ts
Framework support
React, Vue, Svelte, Solid
Used by
Vitest, Astro, Nuxt, SvelteKit

Why it matters

Why Vite replaced the old bundlers

Instant dev server

The browser loads your modules over native ESM, so the server starts in milliseconds regardless of project size.

Optimised production builds

Rollup produces tree-shaken, code-split, minified bundles with hashed filenames for caching.

One shared config

The same config powers dev, build, tests and framework tooling, so there is nothing to duplicate.

The big picture

The three ideas behind Vite

A dev server that uses native ES modules, a Rollup build for production, and one config shared across the whole toolchain.

The dev server

Serve

Serves source files as native ES modules and transforms them on demand.

The build

Bundle

Rollup bundles and optimises the app for production, with code splitting and hashing.

Plugins and config

Extend

A plugin API and a single config file cover frameworks, CSS tools and custom transforms.

Vite at a glance

The core of Vite

vite and vite build

One command for the dev server, another for the production build.

vite.config.ts

Plugins, aliases, server and build options in one file.

Plugins

Framework and tooling integrations share a common plugin interface.

import.meta.env

Environment variables exposed with a VITE_ prefix.

Asset imports

Import images, fonts and CSS directly from JavaScript.

Code splitting

Dynamic import creates separate chunks loaded on demand.

A short history

From a fast dev server to the default toolchain

  1. 2020

    Vite released

    Evan You introduces a dev server built on native ES modules.

    20
  2. 2021

    Rapid adoption

    Vue, React, Svelte and others adopt Vite as their recommended tooling.

    21
  3. 2022

    Vite 3 and 4

    Rollup 3, a stable plugin API and faster builds ship.

    22
  4. 2024

    Vite 5 and 6

    Continued performance work and a Rust-based option via Rolldown.

    24
  5. Today

    The default toolchain

    Used directly and as the engine inside Vitest, Astro, Nuxt and SvelteKit.

    Today

The complete guide

Vite: Everything you need to know

What is Vite?

Vite is a build tool with two jobs: a development server that is almost instantly ready, and a production build that produces optimised bundles. It was created by Evan You in 2020 and has become the default toolchain for most modern front-end frameworks.

The key insight is that development and production have different bottlenecks. In development you want fast startup and fast updates, so Vite does not bundle at all — it serves your source over native ES modules and transforms files on demand. In production you want the smallest, fastest output, so Vite uses Rollup to bundle and optimise.

The development server

Running vite starts a dev server that serves your source as native ES modules. The browser requests each module, and Vite transforms it on the fly.

# terminal
vite          # start the dev server
vite build    # production build
vite preview  # preview the production build locally

Because there is no full bundle to produce, startup is measured in milliseconds and stays constant as the project grows. Dependencies are pre-bundled once with esbuild, since they change rarely, which avoids a flood of small requests. Hot module replacement then updates only the modules that changed, preserving application state where possible.

The production build

vite build runs the Rollup pipeline and writes the result to dist by default.

  • Tree-shaking removes unused exports.
  • Code splitting produces chunks for routes and dynamic imports.
  • Minification shrinks JavaScript, CSS and HTML.
  • Asset hashing appends content hashes for long-term caching.
  • CSS handling extracts and minifies stylesheets.

The output is a set of static files you can deploy to any host, CDN or static platform.

Configuration

Vite reads a vite.config.ts file at the project root.

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: { "@": "/src" },
  },
  server: {
    port: 5173,
    proxy: {
      "/api": "http://localhost:8787",
    },
  },
  build: {
    outDir: "dist",
    sourcemap: true,
  },
});

The resolve.alias option creates import shortcuts, and server.proxy forwards API requests to a backend during development, which avoids CORS issues. Most framework setups generate this file for you and only need small additions.

Plugins

Plugins are how Vite supports frameworks and tooling. They implement a Rollup-compatible interface with additional Vite-specific hooks.

// plugins.ts
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [vue()],
});

Common plugins cover React, Vue, Svelte and Solid, plus CSS tools such as Tailwind, legacy browser support, PWA features and bundle analysis. Because the interface is Rollup-compatible, a large portion of the Rollup plugin ecosystem works directly.

Environment variables

Vite loads variables from .env files and exposes only those prefixed with VITE_ to client code.

# .env
VITE_API_URL=https://api.example.com
DB_PASSWORD=secret
// env.ts
const url = import.meta.env.VITE_API_URL;
const isDev = import.meta.env.DEV;

The VITE_ prefix is a safety mechanism: anything without it stays out of the client bundle, so secrets cannot leak by accident. Variables are replaced at build time, not read at runtime, and are accessed through import.meta.env.

Assets and CSS

You import assets directly from JavaScript, and Vite handles the rest.

// assets.ts
import logo from "./logo.svg";
import "./styles.css";
import styles from "./Button.module.css";

Small assets are inlined as data URLs, larger ones are emitted with hashed names, and CSS imported this way is bundled and, in development, hot-reloaded. The public directory is for files that must keep an exact path, such as robots.txt or a favicon referenced by URL.

Code splitting

Dynamic import() creates a separate chunk loaded on demand, which is the main lever for keeping the initial bundle small.

// lazy.ts
const { Chart } = await import("./Chart");

Apply it to routes, modals, editors and any feature that is not needed on first paint. Frameworks built on Vite often handle route-level splitting for you, but understanding the primitive helps when you need finer control.

Beyond the app

Vite is not only for single-page apps. Library mode builds a distributable package with the right formats and externalised dependencies. Vite also supports server-side rendering, and it is the engine inside Vitest, Astro, Nuxt and SvelteKit. That shared foundation is why the same config, aliases and plugins work across development, testing and building.

Best practices

  • Keep the config minimal and let framework plugins handle the details.
  • Use resolve.alias for clean imports instead of long relative paths.
  • Proxy the API in development rather than hardcoding a host.
  • Prefix client env vars with VITE_ and keep secrets unprefixed.
  • Split heavy features with dynamic imports.
  • Import assets from JavaScript so they get hashed and optimised.
  • Enable source maps for production if you use an error tracker.

Common mistakes

  • Expecting process.env in client code instead of import.meta.env.
  • Exposing secrets by prefixing them with VITE_.
  • Putting files in public that should be imported and hashed.
  • Shipping one enormous bundle by never using dynamic imports.
  • Adding plugins that duplicate what a framework plugin already does.
  • Fighting the config instead of using a framework preset.

Where to go next

Vite is the foundation of the modern toolchain. Compare it with Webpack for older projects, run tests with Vitest, and see it power frameworks in the React, Astro and SvelteKit guides. Then open a project’s vite.config.ts and make one deliberate change to see the effect.

Loading assets

Import assets from JavaScript so the bundler fingerprints and optimises them. The public folder is for files that must keep an exact path.

Prefer
import logo from "./logo.svg";

// hashed, optimised,
// tree-shaken when unused
<img src={logo} alt="Logo" />;
Avoid
// no hashing, no optimisation,
// easy to reference a
// file that does not exist
<img src="/logo.svg" alt="Logo" />;

Environment variables

Only VITE_-prefixed variables are exposed to the client, and they are read from import.meta.env.

Prefer
// .env
// VITE_API_URL=https://api.example.com

const url = import.meta.env.VITE_API_URL;
Avoid
// process.env is not defined
// in the browser bundle
const url = process.env.API_URL;

FAQ

Frequently asked questions

Keep learning

Related topics from the roadmap.

$ start learning

Ready to start learning Vite?

Our interactive tutorial walks you through Vite step by step — with quizzes and real code you can run in the browser.