Vite has become the default build tool for modern frontend projects. Its dev server starts instantly because it serves source files as native ES modules—no bundling required. Hot Module Replacement (HMR) updates in milliseconds. Production builds use Rollup for optimized output. Vite 5 improves on this foundation with better stability and plugin compatibility.
Why Vite?
Traditional bundlers like Webpack bundle everything before the dev server starts. As projects grow, this takes longer:
| Project size | Webpack cold start | Vite cold start |
|---|---|---|
| Small | ~5s | ~300ms |
| Medium | ~15s | ~500ms |
| Large | ~60s+ | ~1s |
Vite's approach:
- Pre-bundle dependencies with esbuild (fast)
- Serve source files as ES modules
- Transform only requested files
- HMR updates only changed modules
Creating a project
Use the create-vite scaffold:
# npm
npm create vite@latest my-app
# pnpm
pnpm create vite my-app
# bun
bun create vite my-app
Choose your framework and variant:
| Template | Description |
|---|---|
vanilla |
Plain JavaScript |
vanilla-ts |
Plain TypeScript |
react |
React with JavaScript |
react-ts |
React with TypeScript |
vue |
Vue 3 |
svelte |
Svelte |
solid |
SolidJS |
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
Project structure
my-app/
├── index.html # Entry point
├── package.json
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript config
├── public/ # Static assets (copied as-is)
└── src/
├── main.tsx # App entry
├── App.tsx # Root component
└── index.css # Global styles
Unlike Webpack, index.html is at the root and is the true entry point. Vite processes it and injects scripts.
Configuration
Configure Vite in vite.config.ts:
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'),
'@components': path.resolve(__dirname, './src/components'),
},
},
server: {
port: 3000,
open: true,
},
build: {
outDir: 'dist',
sourcemap: true,
},
});
Path aliases
Set up path aliases for cleaner imports:
// vite.config.ts
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
Update tsconfig.json to match:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
Now import with:
import { Button } from '@/components/Button';
Environment variables
Vite exposes environment variables prefixed with VITE_:
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
Access in code:
const apiUrl = import.meta.env.VITE_API_URL;
const title = import.meta.env.VITE_APP_TITLE;
| Prefix | Exposed to | Use for |
|---|---|---|
VITE_ |
Client | Public config |
| None | Build only | Secrets (don't use in client) |
CSS handling
Vite supports CSS out of the box:
| Format | Support |
|---|---|
| CSS | Built-in |
| CSS Modules | .module.css files |
| PostCSS | Auto-detected with postcss.config.js |
| Sass/SCSS | Install sass |
| Less | Install less |
// CSS Modules
import styles from './Button.module.css';
function Button() {
return <button className={styles.button}>Click</button>;
}
Adding Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Configure tailwind.config.js:
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
Import in src/index.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
Production build
Build for production:
npm run build
Output goes to dist/. Vite:
- Bundles with Rollup
- Tree-shakes unused code
- Minifies JavaScript and CSS
- Generates hashed filenames for caching
Preview the production build:
npm run preview
Build optimization
Configure build settings:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
},
},
},
chunkSizeWarningLimit: 500,
},
});
Static assets
Files in public/ are copied to build output:
public/
├── favicon.ico
├── robots.txt
└── images/
└── logo.png
Reference with absolute paths:
<img src="/images/logo.png" alt="Logo" />
Files in src/assets/ are processed (hashed, optimized):
import logo from './assets/logo.png';
<img src={logo} alt="Logo" />
Plugins
Extend Vite with plugins:
| Plugin | Purpose |
|---|---|
@vitejs/plugin-react |
React Fast Refresh |
@vitejs/plugin-vue |
Vue SFC support |
vite-plugin-svgr |
Import SVG as React components |
vite-plugin-pwa |
Progressive Web App |
unplugin-auto-import |
Auto-import APIs |
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [react(), svgr()],
});
Summary
Vite provides instant dev server startup and fast HMR through native ES modules. Create projects with npm create vite, configure with vite.config.ts, and build with Rollup for production. Path aliases, environment variables, and CSS handling work out of the box. Add plugins for specific needs. For SPAs without SSR, Vite is the modern standard.
