Vite: The Complete Guide for 2026
Webpack dominated frontend tooling for nearly a decade, but its fundamental architecture — bundle everything before serving anything — creates a painful bottleneck as projects grow. A 50,000-module application can take 30 seconds or more to start the dev server. Vite solves this by exploiting native ES modules in the browser: instead of bundling your entire app before the first page load, Vite serves source files on demand. The result is a dev server that starts in milliseconds regardless of project size, with hot module replacement that stays fast as your codebase scales.
This guide covers Vite from project scaffolding through production optimization. Whether you are starting a new project or migrating from webpack or Create React App, you will find practical, copy-paste-ready examples for every scenario.
Table of Contents
- What is Vite and Why It Is Faster
- Getting Started (React, Vue, Svelte, Vanilla)
- vite.config.ts Configuration
- Development Server Features
- Building for Production
- Plugins Ecosystem
- CSS Handling
- Static Asset Handling
- Library Mode
- SSR Support
- Migrating from Webpack and CRA
- Performance Optimization Tips
- Vite vs Alternatives
- Frequently Asked Questions
1. What is Vite and Why It Is Faster
Vite (French for "fast", pronounced /vit/) is a frontend build tool created by Evan You, the creator of Vue.js. It consists of two parts: a development server that serves your source code over native ES modules with blazing-fast Hot Module Replacement, and a build command that bundles your code with Rollup for production.
The Webpack Bottleneck
Traditional bundlers like webpack work by crawling your entire module graph, transforming every file, and producing a bundle before the dev server can serve anything. As your project grows, startup time grows linearly:
# Webpack's approach (simplified)
1. Parse entry point
2. Recursively resolve ALL imports (thousands of modules)
3. Transform every module (Babel, TypeScript, etc.)
4. Bundle everything into memory
5. THEN start the dev server
# Result: 10-60 seconds for large projects
Vite's Architecture
Vite takes a fundamentally different approach by splitting work into two categories:
- Dependencies — third-party code in
node_modulesthat rarely changes. Vite pre-bundles these using esbuild, which is 10-100x faster than JavaScript-based bundlers - Source code — your application code that changes frequently. Vite serves this as native ES modules, transforming individual files on demand only when the browser requests them
# Vite's approach (simplified)
1. Pre-bundle dependencies with esbuild (once, cached)
2. Start the dev server immediately
3. Browser requests a module → Vite transforms just that file
4. Only the modules visible on screen are processed
# Result: <300ms startup regardless of project size
This means the browser does the work of module resolution. When you load a page, only the modules imported by that page are transformed and served. Navigate to a different route, and only those new modules are processed. The dev server never needs to know about your entire application upfront.
2. Getting Started
Vite provides official templates for every major framework. The create vite scaffolding tool sets up a project with the correct plugins and configuration in seconds.
Scaffolding a New Project
# Interactive mode — choose framework and variant
npm create vite@latest my-app
# Direct templates (skip prompts)
npm create vite@latest my-react-app -- --template react-ts
npm create vite@latest my-vue-app -- --template vue-ts
npm create vite@latest my-svelte-app -- --template svelte-ts
npm create vite@latest my-vanilla-app -- --template vanilla-ts
# With pnpm or yarn
pnpm create vite my-app --template react-ts
yarn create vite my-app --template react-ts
Available Templates
# JavaScript variants
vanilla, vue, react, preact, lit, svelte, solid, qwik
# TypeScript variants
vanilla-ts, vue-ts, react-ts, preact-ts, lit-ts, svelte-ts, solid-ts, qwik-ts
React + TypeScript Project Structure
my-react-app/
├── index.html # Entry HTML (in project root, not public/)
├── package.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts # Vite configuration
├── public/
│ └── vite.svg # Static assets (served as-is)
└── src/
├── main.tsx # Application entry point
├── App.tsx
├── App.css
├── index.css
└── vite-env.d.ts # TypeScript type declarations for Vite
The key difference from webpack projects: index.html lives in the project root, not in a public folder. It is the true entry point, and Vite processes it directly:
<!-- index.html — note the script tag with type="module" -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Running the Dev Server
cd my-react-app
npm install
npm run dev
# Output:
# VITE v6.x.x ready in 127ms
# ➜ Local: http://localhost:5173/
# ➜ Network: http://192.168.1.10:5173/
3. vite.config.ts Configuration
Vite is configured through a vite.config.ts (or .js, .mjs) file in the project root. The defineConfig helper provides full TypeScript IntelliSense without additional type imports.
// vite.config.ts — comprehensive example
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
// Plugins
plugins: [react()],
// Dev server options
server: {
port: 3000,
open: true, // open browser on start
host: true, // listen on all addresses (for Docker)
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// Build options
build: {
outDir: 'dist',
sourcemap: true, // generate source maps
target: 'es2022', // browser compatibility target
minify: 'esbuild', // 'esbuild' (fast) or 'terser' (smaller)
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
},
// Module resolution
resolve: {
alias: {
'@': '/src',
'@components': '/src/components',
'@utils': '/src/utils',
},
},
// CSS options
css: {
modules: {
localsConvention: 'camelCase',
},
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables" as *;`,
},
},
},
// Environment variable prefix (default is VITE_)
envPrefix: 'VITE_',
// Preview server (for testing production builds locally)
preview: {
port: 4173,
},
})
Conditional Configuration
Use the function form to access the current mode and command:
// vite.config.ts — conditional config
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig(({ command, mode }) => {
const isDev = command === 'serve'
const isProd = mode === 'production'
return {
plugins: [react()],
build: {
sourcemap: isDev ? 'inline' : isProd ? false : true,
minify: isProd ? 'esbuild' : false,
},
server: {
proxy: isDev ? { '/api': 'http://localhost:8080' } : undefined,
},
}
})
4. Development Server Features
Hot Module Replacement (HMR)
Vite's HMR updates modules in the browser without a full page reload. When you edit a React component, only that component re-renders — application state, scroll position, and form inputs are preserved. HMR stays fast regardless of application size because Vite only needs to invalidate the changed module and its direct importers, not the entire bundle.
// HMR is automatic for supported file types:
// - .jsx/.tsx with React Fast Refresh (via @vitejs/plugin-react)
// - .vue files with Vue SFC hot reload
// - .svelte files with Svelte HMR
// - .css files (injected without page reload)
// For custom HMR handling in plain modules:
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// Handle the updated module
})
}
API Proxy
Proxy API requests to your backend during development to avoid CORS issues:
// vite.config.ts
export default defineConfig({
server: {
proxy: {
// String shorthand
'/api': 'http://localhost:8080',
// Full options
'/api/v2': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/v2/, '/v2'),
secure: false, // allow self-signed certs
},
// WebSocket proxy
'/ws': {
target: 'ws://localhost:8080',
ws: true,
},
},
},
})
Environment Variables
Vite exposes environment variables on import.meta.env. Only variables prefixed with VITE_ are exposed to client code (preventing accidental leakage of secrets):
# .env — loaded in all modes
VITE_APP_TITLE=My App
# .env.development — loaded in dev mode only
VITE_API_URL=http://localhost:8080
# .env.production — loaded in production builds
VITE_API_URL=https://api.example.com
# .env.local — local overrides (gitignored)
VITE_API_KEY=my-dev-key
// Accessing environment variables in code
console.log(import.meta.env.VITE_APP_TITLE) // "My App"
console.log(import.meta.env.VITE_API_URL) // depends on mode
console.log(import.meta.env.MODE) // "development" or "production"
console.log(import.meta.env.DEV) // true in dev
console.log(import.meta.env.PROD) // true in production
console.log(import.meta.env.BASE_URL) // base public path
// TypeScript type declarations (src/vite-env.d.ts)
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_API_URL: string
readonly VITE_API_KEY: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
HTTPS in Development
// vite.config.ts — enable HTTPS with auto-generated certs
import basicSsl from '@vitejs/plugin-basic-ssl'
export default defineConfig({
plugins: [basicSsl()],
server: {
https: true,
},
})
5. Building for Production
Vite uses Rollup for production builds, producing highly optimized bundles with tree shaking, code splitting, and minification out of the box.
# Build for production
npm run build
# or
npx vite build
# Preview the production build locally
npm run preview
# or
npx vite preview
Code Splitting
Vite automatically code-splits at dynamic import boundaries. Use React.lazy or dynamic import() to split routes:
// Automatic code splitting with React.lazy
import { lazy, Suspense } from 'react'
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
)
}
// Each route becomes a separate chunk loaded on demand
Manual Chunks
// vite.config.ts — control chunk splitting
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// Group vendor libraries into named chunks
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
'vendor-ui': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
'vendor-charts': ['recharts', 'd3'],
},
},
},
},
})
Tree Shaking
Rollup's tree shaking removes unused exports. To get the best results, use ES module imports and avoid barrel files that re-export everything:
// GOOD: direct import — tree-shakeable
import { format } from 'date-fns'
import { debounce } from 'lodash-es' // note: lodash-es, not lodash
// BAD: namespace import — includes everything
import * as dateFns from 'date-fns'
import _ from 'lodash' // CommonJS, not tree-shakeable
Build Target and Browser Compatibility
// vite.config.ts
export default defineConfig({
build: {
// Modern browsers (default)
target: 'es2022',
// Support older browsers with @vitejs/plugin-legacy
// target: 'es2015',
},
})
// For legacy browser support, add the legacy plugin:
// npm install @vitejs/plugin-legacy
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
legacy({
targets: ['defaults', 'not IE 11'],
}),
],
})
6. Plugins Ecosystem
Vite's plugin API extends Rollup's plugin interface with additional Vite-specific hooks. This means most Rollup plugins work with Vite out of the box, and the Vite ecosystem adds hundreds more.
Official Plugins
# Framework plugins (pick one)
@vitejs/plugin-react # React with Babel
@vitejs/plugin-react-swc # React with SWC (faster)
@vitejs/plugin-vue # Vue 3
@vitejs/plugin-vue-jsx # Vue 3 JSX support
# Utility plugins
@vitejs/plugin-legacy # Legacy browser support
@vitejs/plugin-basic-ssl # HTTPS in development
Essential Community Plugins
// vite.config.ts — common plugin setup
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tsconfigPaths from 'vite-tsconfig-paths'
import svgr from 'vite-plugin-svgr'
import { compression } from 'vite-plugin-compression2'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
// React with SWC (20x faster than Babel)
react(),
// Resolve paths from tsconfig.json (replaces manual aliases)
tsconfigPaths(),
// Import SVGs as React components
svgr(),
// Generate gzip/brotli compressed assets
compression({ algorithm: 'gzip' }),
compression({ algorithm: 'brotliCompress' }),
// Bundle size visualization (generates stats.html)
visualizer({ open: true, gzipSize: true }),
],
})
Popular Plugin Categories
# Testing
vitest # Vite-native test runner (Jest-compatible API)
# PWA
vite-plugin-pwa # Service worker, manifest, offline support
# API Mocking
vite-plugin-mock # Mock API endpoints during development
# Auto-imports
unplugin-auto-import # Auto-import APIs (ref, computed, useState, etc.)
unplugin-vue-components # Auto-import Vue components
# Icons
unplugin-icons # Use any icon set as components
# Bundle Analysis
rollup-plugin-visualizer # Visual bundle analysis
vite-bundle-analyzer # Interactive treemap of bundle contents
7. CSS Handling
Vite has first-class CSS support with no configuration required for most use cases. Import CSS files directly in your JavaScript, and Vite handles the rest.
Basic CSS Imports
// Importing CSS — automatically injected into the page
import './styles.css'
import './reset.css'
// CSS is code-split along with the JavaScript that imports it
// Each lazy-loaded route gets its own CSS chunk
CSS Modules
/* Button.module.css — any file ending in .module.css */
.button {
background: #3b82f6;
color: white;
padding: 0.5rem 1rem;
border-radius: 4px;
}
.primary {
background: #2563eb;
}
.disabled {
opacity: 0.5;
pointer-events: none;
}
// Using CSS modules in React
import styles from './Button.module.css'
function Button({ variant, disabled, children }) {
return (
<button className={`${styles.button} ${styles[variant]} ${disabled ? styles.disabled : ''}`}>
{children}
</button>
)
}
Preprocessors (Sass, Less, Stylus)
# Just install the preprocessor — no plugin or config needed
npm install -D sass
npm install -D less
npm install -D stylus
// Then import directly
import './styles.scss'
import './styles.less'
import './styles.styl'
// CSS modules work with preprocessors too
import styles from './Component.module.scss'
PostCSS
// postcss.config.js — Vite auto-detects this file
export default {
plugins: {
'tailwindcss': {},
'autoprefixer': {},
'postcss-nesting': {}, // native CSS nesting polyfill
},
}
Global CSS Variables with Preprocessors
// vite.config.ts — inject shared variables into every Sass file
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `
@use "@/styles/variables" as *;
@use "@/styles/mixins" as *;
`,
},
},
},
})
8. Static Asset Handling
Vite handles static assets intelligently based on how you import them and their file size.
// Import as URL — Vite adds a content hash for cache busting
import logoUrl from './logo.png'
//=> "/assets/logo-a1b2c3d4.png"
// Import as string (add ?raw suffix)
import shaderCode from './shader.glsl?raw'
// Import as worker
import MyWorker from './worker.js?worker'
// Import as inline base64 (add ?inline suffix)
import iconDataUrl from './icon.svg?inline'
The public Directory
# Files in public/ are served at the root path as-is
# They are NOT processed by Vite (no hashing, no transformation)
public/
├── favicon.ico → /favicon.ico
├── robots.txt → /robots.txt
└── images/
└── og-image.png → /images/og-image.png
# Use public/ for files that:
# - Must keep their exact filename (robots.txt, favicon.ico)
# - Are referenced in index.html directly
# - Need a predictable URL
Asset Inlining
// vite.config.ts — configure asset inlining threshold
export default defineConfig({
build: {
// Files smaller than this (in bytes) are inlined as base64
// Default: 4096 (4kb)
assetsInlineLimit: 4096,
},
})
JSON Imports
// Import the full JSON object
import data from './data.json'
// Named imports — Vite tree-shakes unused fields
import { version, dependencies } from './package.json'
9. Library Mode
Vite can build reusable libraries that others install via npm. Library mode produces ES module and UMD outputs, externalizes dependencies, and generates TypeScript declarations.
// vite.config.ts — library mode
import { defineConfig } from 'vite'
import { resolve } from 'path'
import dts from 'vite-plugin-dts'
export default defineConfig({
plugins: [
dts({ rollupTypes: true }), // generate .d.ts files
],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'MyLibrary', // global name for UMD build
formats: ['es', 'umd'],
fileName: (format) => `my-library.${format}.js`,
},
rollupOptions: {
// Externalize dependencies that consumers will provide
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
},
})
// package.json for a library
{
"name": "my-library",
"version": "1.0.0",
"type": "module",
"main": "./dist/my-library.umd.js",
"module": "./dist/my-library.es.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/my-library.es.js",
"require": "./dist/my-library.umd.js",
"types": "./dist/index.d.ts"
},
"./styles": "./dist/style.css"
},
"files": ["dist"],
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
}
}
10. SSR Support
Vite has built-in support for server-side rendering. It can load and transform ESM-based source code in Node.js, sharing the same plugin pipeline as the client build.
// server.js — basic SSR setup with Express
import express from 'express'
import { createServer as createViteServer } from 'vite'
async function startServer() {
const app = express()
// Create Vite dev server in middleware mode
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom',
})
// Use Vite's middleware for HMR and asset serving
app.use(vite.middlewares)
app.use('*', async (req, res) => {
const url = req.originalUrl
// 1. Read and transform index.html
let template = fs.readFileSync('index.html', 'utf-8')
template = await vite.transformIndexHtml(url, template)
// 2. Load the server entry (Vite transforms it on the fly)
const { render } = await vite.ssrLoadModule('/src/entry-server.tsx')
// 3. Render the app to HTML
const appHtml = await render(url)
// 4. Inject the rendered HTML into the template
const html = template.replace('<!--ssr-outlet-->', appHtml)
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})
app.listen(3000)
}
startServer()
// vite.config.ts — SSR build configuration
export default defineConfig({
build: {
ssr: true, // or specify entry: 'src/entry-server.tsx'
rollupOptions: {
input: 'src/entry-server.tsx',
},
},
ssr: {
// Externalize dependencies that should not be bundled for SSR
external: ['express'],
// Force certain deps to be bundled (if they use browser APIs conditionally)
noExternal: ['some-browser-lib'],
},
})
Production SSR builds produce a server bundle that can be run with Node.js. Frameworks like Nuxt 3, SvelteKit, Astro, and Remix all use Vite's SSR capabilities under the hood, so you rarely need to implement SSR from scratch.
11. Migrating from Webpack and CRA
From Create React App
# Step 1: Install Vite and remove CRA
npm uninstall react-scripts
npm install -D vite @vitejs/plugin-react
# Step 2: Move index.html to project root
mv public/index.html ./index.html
# Step 3: Edit index.html — add the entry script
# Add before </body>:
# <script type="module" src="/src/index.tsx"></script>
# Remove %PUBLIC_URL% references:
# href="%PUBLIC_URL%/favicon.ico" → href="/favicon.ico"
// Step 4: Create vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: { port: 3000 },
})
// Step 5: Update package.json scripts
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
}
}
# Step 6: Replace environment variables
# Find and replace across your codebase:
# process.env.REACT_APP_ → import.meta.env.VITE_
# process.env.NODE_ENV → import.meta.env.MODE
#
# Rename .env variables:
# REACT_APP_API_URL → VITE_API_URL
From Webpack
# Key differences to address:
#
# 1. Entry point
# webpack: entry in webpack.config.js
# vite: <script type="module"> in index.html
#
# 2. Loaders → Plugins or native support
# css-loader, style-loader → built-in
# sass-loader → just install sass
# file-loader, url-loader → built-in (import as URL)
# babel-loader → built-in (esbuild) or plugin
# ts-loader → built-in (esbuild)
#
# 3. require() → import
# require('./image.png') → import imgUrl from './image.png'
# require.context → import.meta.glob
#
# 4. DefinePlugin → env files
# new DefinePlugin({ ... }) → .env files with VITE_ prefix
#
# 5. Aliases
# resolve.alias in webpack → resolve.alias in vite.config.ts
Handling require.context
// webpack: require.context
const images = require.context('./images', false, /\.png$/)
// vite: import.meta.glob
const imageModules = import.meta.glob('./images/*.png', { eager: true })
const images = Object.values(imageModules).map(m => m.default)
// Lazy loading variant
const lazyImages = import.meta.glob('./images/*.png')
// Each value is a () => Promise<Module>
12. Performance Optimization Tips
Dependency Pre-Bundling
// vite.config.ts — optimize dependency pre-bundling
export default defineConfig({
optimizeDeps: {
// Force include deps that Vite might miss
include: ['lodash-es', 'axios'],
// Exclude deps that are already ESM
exclude: ['@vueuse/core'],
},
})
Chunk Strategy
// vite.config.ts — keep chunks small and cacheable
export default defineConfig({
build: {
chunkSizeWarningLimit: 500, // warn above 500KB
rollupOptions: {
output: {
manualChunks(id) {
// Split node_modules into a vendor chunk
if (id.includes('node_modules')) {
// Group by package name
const pkg = id.split('node_modules/')[1].split('/')[0]
if (['react', 'react-dom'].includes(pkg)) return 'vendor-react'
if (['d3', 'recharts'].includes(pkg)) return 'vendor-charts'
return 'vendor'
}
},
},
},
},
})
Avoid Large Dependencies
# Replace heavy libraries with lighter alternatives
lodash (71KB) → lodash-es (tree-shakeable) or individual imports
moment (290KB) → date-fns (tree-shakeable) or dayjs (2KB)
faker.js (1.2MB) → @faker-js/faker (tree-shakeable, import specific locales)
Analyze Bundle Size
# Install the visualizer plugin
npm install -D rollup-plugin-visualizer
# Build with analysis
npx vite build
# Opens an interactive treemap showing what is in each chunk
Asset Optimization
// Use dynamic imports for heavy, non-critical modules
const loadEditor = () => import('monaco-editor')
const loadChart = () => import('recharts')
// Preload critical chunks with modulepreload
// Vite automatically adds modulepreload links for entry chunks
13. Vite vs Alternatives
┌────────────────────┬───────────────┬──────────────┬──────────────┬──────────────┐
│ Feature │ Vite │ webpack │ esbuild │ Turbopack │
├────────────────────┼───────────────┼──────────────┼──────────────┼──────────────┤
│ Dev startup │ <300ms │ 5-60s │ ~100ms │ <500ms │
│ HMR speed │ <50ms │ 100-2000ms │ N/A │ <50ms │
│ Production bundler │ Rollup │ webpack │ esbuild │ webpack │
│ Config complexity │ Minimal │ Complex │ Minimal │ Minimal │
│ Plugin ecosystem │ Large (Rollup)│ Massive │ Small │ Small │
│ CSS support │ Built-in │ Via loaders │ Basic │ Built-in │
│ SSR support │ Built-in │ Via plugins │ No │ Via Next.js │
│ Framework support │ All major │ All major │ Framework- │ Next.js only │
│ │ │ │ agnostic │ (currently) │
│ Tree shaking │ Excellent │ Good │ Good │ Good │
│ Code splitting │ Automatic │ Automatic │ Manual │ Automatic │
│ Maturity │ Production │ Battle-tested│ Production │ Stabilizing │
│ Learning curve │ Low │ High │ Low │ Low │
└────────────────────┴───────────────┴──────────────┴──────────────┴──────────────┘
Choose Vite when: You want the best developer experience with minimal configuration. It works with React, Vue, Svelte, Solid, and vanilla JS/TS. It is the default for Vue (via create-vue), SvelteKit, Nuxt 3, Astro, and many other frameworks.
Choose webpack when: You have a large existing webpack project with complex custom loaders and plugins that have no Vite equivalents. Migration may not be worth the effort for stable projects nearing end of life.
Choose esbuild when: You need a low-level, extremely fast bundler for simple projects, CLI tools, or Node.js libraries. Esbuild lacks HMR, CSS code splitting, and the framework integrations that Vite provides.
Choose Turbopack when: You are building a Next.js application and want to use Vercel's Rust-based bundler. Turbopack is tightly integrated with Next.js but not yet a general-purpose build tool.
Frequently Asked Questions
Why is Vite so much faster than webpack?
Vite is faster for two reasons. In development, it serves source files as native ES modules directly to the browser, transforming individual files on demand with esbuild (10-100x faster than Babel). Webpack must bundle your entire application before serving anything. In production, Vite uses Rollup for optimized builds. The result is near-instant dev server startup and sub-second HMR regardless of project size.
Can I use Vite with React, Vue, and Svelte?
Yes. Vite has first-class support for all three frameworks through official plugins: @vitejs/plugin-react for React, @vitejs/plugin-vue for Vue 3, and @sveltejs/vite-plugin-svelte for Svelte. Scaffold a new project with npm create vite@latest and select your preferred template. Vite also supports vanilla JavaScript and TypeScript without any framework.
How do I migrate from Create React App to Vite?
Replace react-scripts with vite and @vitejs/plugin-react. Move index.html from public/ to the project root and add a <script type="module" src="/src/index.tsx"> tag. Create a vite.config.ts with the React plugin. Replace process.env.REACT_APP_ references with import.meta.env.VITE_ and rename your environment variables accordingly. Most projects complete the migration in under an hour.
What is the difference between Vite and esbuild?
Esbuild is a low-level, fast JavaScript bundler written in Go. Vite is a higher-level build tool that uses esbuild internally for dependency pre-bundling and TypeScript transformation during development, but uses Rollup for production builds. Vite provides the complete developer experience: dev server, HMR, plugin system, CSS handling, asset processing, and framework integrations. You would not typically use esbuild directly for application development.
Does Vite support server-side rendering (SSR)?
Yes. Vite has built-in SSR support with the ssrLoadModule API for server-side module loading with HMR during development. For production, Vite builds a separate SSR bundle optimized for Node.js. Frameworks like Nuxt 3, SvelteKit, Astro, and Remix all use Vite's SSR capabilities. You can also implement custom SSR setups using Vite's low-level APIs with Express, Fastify, or any Node.js server.
Conclusion
Vite has earned its position as the default frontend build tool for modern web development. Its architecture — native ES modules for development, Rollup for production — delivers the fast feedback loops developers need without sacrificing build quality. Whether you are starting a new React app, building a Vue component library, or migrating a legacy webpack project, Vite's minimal configuration and rich plugin ecosystem make the transition straightforward.
Start with npm create vite@latest, explore the dev server's HMR and proxy features, and gradually customize your vite.config.ts as your project grows. The official Vite documentation at vite.dev covers every option in detail.
Learn More
- TypeScript: The Complete Guide — master TypeScript, which Vite supports out of the box with zero config
- React Hooks: The Complete Guide — deep dive into React hooks for Vite+React projects
- CSS Grid: The Complete Guide — modern CSS layout techniques for your Vite-powered apps
- Node.js: The Complete Guide — server-side JavaScript for Vite SSR setups
- npm and Yarn: The Complete Guide — package management for Vite projects
- npm Commands Cheat Sheet — quick reference for package management