vite
Vite Configuration Patterns
Basic Setup
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
});
Use defineConfig for type safety and IntelliSense.
Conditional Configuration
export default defineConfig(({ command, mode }) => {
const isDev = command === "serve";
const isProd = mode === "production";
return {
plugins: [react()],
build: {
sourcemap: isDev,
minify: isProd ? "esbuild" : false,
},
};
});
commandis"serve"(dev) or"build"(production).modeis"development","production", or custom.
Path Aliases
import { resolve } from "path";
export default defineConfig({
resolve: {
alias: {
"@": resolve(__dirname, "src"),
"@components": resolve(__dirname, "src/components"),
"@lib": resolve(__dirname, "src/lib"),
},
},
});
Match aliases in tsconfig.json:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@lib/*": ["./src/lib/*"],
},
},
}
Environment Variables
Variables must be prefixed with VITE_ to be exposed to client code:
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
# Not exposed to client (server-only)
DATABASE_URL=postgres://...
API_SECRET=secret
// Access in code
const apiUrl = import.meta.env.VITE_API_URL;
const mode = import.meta.env.MODE; // "development" | "production"
const isDev = import.meta.env.DEV; // boolean
const isProd = import.meta.env.PROD; // boolean
Type-Safe Env
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Env File Priority
.env # loaded in all cases
.env.local # loaded in all cases, gitignored
.env.[mode] # loaded for specific mode
.env.[mode].local # loaded for specific mode, gitignored
Dev Server Proxy
Proxy API requests to a backend server:
export default defineConfig({
server: {
port: 3000,
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
},
"/api/v2": {
target: "http://localhost:8081",
rewrite: (path) => path.replace(/^\/api\/v2/, ""),
},
"/ws": {
target: "ws://localhost:8080",
ws: true,
},
},
},
});
Plugins
Plugin Order
export default defineConfig({
plugins: [
pluginA(), // default order
pluginB({ enforce: "pre" }), // runs before Vite core
pluginC({ enforce: "post" }), // runs after Vite build
],
});
Conditional Plugins
export default defineConfig(({ command }) => ({
plugins: [
react(),
command === "build" && visualizer(), // only during build
command === "serve" && inspector(), // only during dev
].filter(Boolean),
}));
Common Plugins
| Plugin | Purpose |
|---|---|
@vitejs/plugin-react |
React Fast Refresh, JSX transform |
@tailwindcss/vite |
Tailwind CSS v4 integration |
vite-plugin-svgr |
Import SVGs as React components |
vite-plugin-pwa |
Progressive Web App support |
rollup-plugin-visualizer |
Bundle size visualization |
vite-tsconfig-paths |
Auto-resolve tsconfig paths |
Build Optimization
Code Splitting
Vite splits code automatically at dynamic imports. Customize chunking:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
router: ["react-router"],
query: ["@tanstack/react-query"],
},
},
},
},
});
Dynamic Manual Chunks
manualChunks(id) {
if (id.includes("node_modules")) {
if (id.includes("react")) return "vendor";
if (id.includes("@tanstack")) return "tanstack";
return "deps";
}
},
Build Targets
export default defineConfig({
build: {
target: "es2022", // modern browsers
minify: "esbuild", // fast (default), or "terser" for smaller output
cssMinify: "lightningcss", // faster CSS minification
sourcemap: true, // enable for production debugging
reportCompressedSize: false, // skip gzip size reporting (faster builds)
},
});
Chunk Size Warnings
build: {
chunkSizeWarningLimit: 500, // KB, default is 500
},
Library Mode
Build a library for npm distribution:
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, "src/index.ts"),
name: "MyLib",
formats: ["es", "cjs"],
fileName: (format) => `my-lib.${format}.js`,
},
rollupOptions: {
external: ["react", "react-dom"],
output: {
globals: {
react: "React",
"react-dom": "ReactDOM",
},
},
},
},
});
For library bundling, consider tsdown instead — it handles DTS generation, multiple entries, and package.json exports more elegantly.
CSS
CSS Modules
// Automatically enabled for *.module.css files
import styles from "./button.module.css";
// Configure behavior
export default defineConfig({
css: {
modules: {
localsConvention: "camelCaseOnly",
generateScopedName: "[name]__[local]___[hash:base64:5]",
},
},
});
PostCSS
export default defineConfig({
css: {
postcss: "./postcss.config.js",
},
});
Lightning CSS
export default defineConfig({
css: {
transformer: "lightningcss",
lightningcss: {
targets: browserslistToTargets(browserslist(">= 0.25%")),
},
},
});
Static Assets
// Import as URL
import logo from "./logo.svg";
// Import as string
import shaderCode from "./shader.glsl?raw";
// Import as worker
import Worker from "./worker.js?worker";
Public Directory
Files in public/ are served at root and copied as-is to build output. Use for favicons, robots.txt, and files that must keep their exact filename.
Preview
Preview production builds locally:
vite preview --port 4173
export default defineConfig({
preview: {
port: 4173,
strictPort: true,
},
});
Performance Tips
- Dependency pre-bundling: Vite pre-bundles
node_moduleswith esbuild. UseoptimizeDeps.includeto pre-bundle deps that are dynamically imported. - Avoid barrel re-exports: Barrel files (
index.tsexporting everything) defeat tree-shaking in dev. Import directly when possible. - Use
reportCompressedSize: false: Disables gzip size computation for faster builds. - Split vendor chunks: Separate stable vendor code from frequently changing app code.
More from grahamcrackers/skills
bulletproof-react-patterns
Bulletproof React architecture patterns for scalable, maintainable applications. Covers feature-based project structure, component patterns, state management boundaries, API layer design, error handling, security, and testing strategies. Use when structuring a React project, designing application architecture, organizing features, or when the user asks about React project structure or scalable patterns.
44react-aria-components
React Aria Components patterns for building accessible, unstyled UI with composition-based architecture. Covers component structure, styling with Tailwind and CSS, render props, collections, forms, selections, overlays, and drag-and-drop. Use when building accessible components, using react-aria-components, creating design systems, or when the user asks about React Aria, accessible UI primitives, or headless component libraries.
15clean-code-principles
Clean code principles for readable, maintainable TypeScript and React codebases. Covers naming, functions, abstraction, composition, error handling, comments, and code smells. Use when writing new code, refactoring, reviewing code quality, or when the user asks about clean code, readability, or maintainability.
10typescript-best-practices
Core TypeScript conventions for type safety, inference, and clean code. Use when writing TypeScript, reviewing TypeScript code, creating interfaces/types, or when the user asks about TypeScript patterns, conventions, or best practices.
9tanstack-query
TanStack Query v5 patterns for server state management, caching, mutations, optimistic updates, and query organization. Use when working with TanStack Query, React Query, server state, data fetching hooks, or when the user asks about caching strategies, query invalidation, or mutation patterns.
8zustand
Zustand state management patterns for React including store design, selectors, slices, middleware (immer, persist, devtools), and async actions. Use when managing client-side state, creating stores, working with Zustand, or when the user asks about global state management, store patterns, or state persistence.
7