What is Vite? The Build Tool That Made Frontend Development Fast Again

Vite replaced Webpack for a lot of teams without much fanfare. Native ES modules, a dev server that starts in milliseconds, and a production build that doesn't require understanding 300 lines of config. Here's how it works and why the switch is worth it.

Vite is a frontend build tool. It starts your dev server in milliseconds, serves files with instant hot module replacement, and produces optimized production builds with Rollup underneath.

It replaced Webpack for a lot of teams not because Evan You published a convincing argument. Developers tried it, the dev server started in under two seconds, and they stopped going back.

The before and after on a medium-sized React project: 35 seconds to start with Webpack, 1.5 seconds with Vite. That's not marginal. That changes how development feels.

The Webpack Era Problem

Webpack became the standard frontend build tool around 2014. It bundled JavaScript modules, handled CSS and images through loaders, enabled code splitting, and managed tree-shaking. The configuration was complex and fragile. Startup times on large projects hit tens of seconds or worse.

When Webpack started your dev server, it bundled your entire application upfront. Every file. Every dependency. Once the bundle existed, it served it. Any change triggered a partial or full rebuild. That rebuild time scaled with your codebase size.

Fine for small projects. Painful at scale.

Why Vite Starts Fast

Vite doesn't bundle anything during development. That's the whole insight.

Modern browsers support ES modules natively. A browser can process import { something } from './module.js' directly without you pre-bundling anything. Vite exploits this completely. Your source files get served as-is to the browser. The browser handles module resolution.

Your dev server starts immediately because there's nothing to build. The browser requests a file, Vite transforms it on demand (TypeScript compilation, JSX processing, CSS handling) using esbuild, and returns it. On a cold start, you wait for the handful of files your entry point needs, not your entire codebase.

HMR works the same way. You update a component, Vite pushes exactly that module to the browser. Nothing else reruns. Your application state survives.

Development vs. Production: Two Different Tools

Here's what catches people: Vite uses two completely different tools under the hood depending on the environment.

Development uses native ES modules served directly, with on-demand transforms via esbuild. esbuild is written in Go and is 10-100x faster than JavaScript-based bundlers. No bundling at all.

Production uses Rollup for bundling. Rollup produces smaller, more optimized output than esbuild for production use cases. Code splitting, tree-shaking, chunk optimization, hashed filenames.

Your dev and prod pipelines aren't identical. This rarely causes problems. Occasionally you'll see a behavior difference between dev and the production build. Run npm run preview before deploying to catch these.

Getting Started

npm create vite@latest my-project -- --template react-ts
cd my-project
npm install
npm run dev

Official templates cover React, Vue, Svelte, Solid, Preact, and plain JavaScript or TypeScript. Pick the one you need.

Your project structure:

my-project/
  public/          # Static assets served as-is (favicon, robots.txt)
  src/
    main.tsx       # Entry point
    App.tsx
  index.html       # Vite's entry point — lives at root, not in public/
  vite.config.ts
  package.json

index.html lives at the project root and contains a <script type="module" src="/src/main.tsx"> tag. That tag is how Vite knows where your app starts. This trips people up migrating from Webpack, where index.html often lives in public/.

The Config File

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,
    proxy: {
      '/api': 'http://localhost:8080',
    },
  },
});

@vitejs/plugin-react handles JSX transformation and React Fast Refresh, the mechanism that preserves component state across hot reloads. React projects need this plugin.

resolve.alias lets you write import Button from '@/components/Button' from anywhere instead of counting relative paths. Set this up on day one.

server.proxy forwards requests matching a path to another server during development. Your frontend at localhost:3000 can call /api/users and Vite silently proxies it to localhost:8080. No CORS issues in development.

Environment Variables

Vite uses .env files. Variables must be prefixed with VITE_ to reach client code.

# .env.development
VITE_API_URL=http://localhost:8080

# .env.production
VITE_API_URL=https://api.yourapp.com

Access them with:

const apiUrl = import.meta.env.VITE_API_URL;

Vite uses import.meta.env rather than process.env. If you're migrating from Create React App or a Webpack setup, update every process.env.REACT_APP_* reference.

TypeScript

Vite supports TypeScript out of the box and uses esbuild for transpilation. esbuild strips types without type-checking. Your build is fast. Type errors don't block the dev server.

For actual type checking, run tsc --noEmit separately. Most projects add it as a pre-build step:

"scripts": {
  "dev": "vite",
  "build": "tsc --noEmit && vite build",
  "preview": "vite preview"
}

Your CI pipeline catches type errors. Your dev workflow stays fast.

The JavaScript vs TypeScript article covers whether TypeScript is worth the overhead. For Vite projects: yes, add it from the start. The --template react-ts flag does it for you automatically.

CSS Handling

Import a CSS file and Vite injects it into the page. No configuration required.

import './styles.css';

CSS Modules work without any config. Rename the file to .module.css:

import styles from './Button.module.css';

function Button() {
  return <button className={styles.button}>Click</button>;
}

CSS Modules scope class names to the component. Two components can both have a .button class and they won't conflict.

Sass works after installing the preprocessor:

npm install -D sass

No config changes needed. Vite detects the .scss extension and processes it. Same pattern for Less and Stylus.

PostCSS works with a postcss.config.js file at your project root. Tailwind CSS v4 has a dedicated Vite plugin that makes this even simpler, which the What is Tailwind CSS article covers in detail.

Building for Production

npm run build

Output goes to dist/. Rollup handles code splitting and tree-shaking. You get hashed filenames for cache-busting. A React app with moderate dependencies typically produces under 200kb of JavaScript compressed.

Preview your production build locally before deploying:

npm run preview

vite preview serves dist/ at localhost:4173. Run this before every deploy. It catches the handful of cases where dev behavior diverges from production.

Plugins

Vite's plugin API is compatible with Rollup plugins, which extends the available ecosystem considerably.

Common ones worth knowing:

@vitejs/plugin-react handles React JSX and Fast Refresh. Required for React.

rollup-plugin-visualizer generates a treemap of your bundle. Run it before your first deploy. You'll find at least one dependency you didn't know was that large.

vite-plugin-svgr imports SVG files as React components.

vite-plugin-pwa adds service worker and manifest support for Progressive Web Apps.

The plugin ecosystem is smaller than Webpack's but covers every common case. If you need something unusual, check whether the Rollup equivalent works. Most do.

Using Bun With Vite

Bun drops in as a replacement for npm with Vite projects:

bun create vite my-project --template react-ts
cd my-project
bun install
bun run dev

bun install is meaningfully faster than npm install for dependency resolution. The Vite dev server still runs the same way underneath. If startup time and install speed matter to you, this combination is worth using. The What is Bun article covers what Bun actually is and what you're getting.

If you're curious about the full runtime comparison across Node.js, Deno, and Bun and which one belongs in your stack, that comparison article lays out the tradeoffs without the hype.

Migrating from Create React App

Create React App is deprecated. If you're on it, migrating to Vite is worth an afternoon.

The main changes:

  • Move index.html from public/ to the project root and add <script type="module" src="/src/index.tsx"> inside it
  • Replace process.env.REACT_APP_* with import.meta.env.VITE_*
  • Replace react-scripts commands with vite, vite build, vite preview
  • Update tsconfig.json for Vite's module resolution ("moduleResolution": "Bundler")

Most projects migrate in under two hours. The dev experience improvement makes it worth it.

The Conclusion

Vite ended the era of waiting for your dev server. The native ES module approach was right, and the ecosystem caught up fast. It's the standard build tool for new frontend projects.

If you're starting something new, use Vite. If you're on Webpack or Create React App and the startup time is friction in your daily work, migrating is worth the afternoon it takes.