Vite Patterns
vite.config.ts Baseline
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), '')
return {
plugins: [react()],
resolve: {
alias: { '@': resolve(__dirname, './src') },
},
build: {
target: 'es2022',
sourcemap: mode !== 'production',
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
query: ['@tanstack/react-query'],
},
},
},
},
server: {
port: 3000,
proxy: {
'/api': {
target: env.VITE_API_URL ?? 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
preview: { port: 4173 },
}
})
Environment Variables
# .env
VITE_API_URL=http://localhost:8080
VITE_FEATURE_X=true
# .env.production
VITE_API_URL=https://api.example.com
// Access in code (VITE_ prefix exposes to client)
const apiUrl = import.meta.env.VITE_API_URL
const isProd = import.meta.env.PROD // boolean
const isDev = import.meta.env.DEV // boolean
const mode = import.meta.env.MODE // 'development' | 'production' | custom
// Type-safe env
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_FEATURE_X: string
}
interface ImportMeta { readonly env: ImportMetaEnv }
Code Splitting
// Lazy route components
import { lazy, Suspense } from 'react'
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
function App() {
return (
<Suspense fallback={<PageSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
)
}
Glob Imports
// Import all locale files
const locales = import.meta.glob('./locales/*.json', { eager: true })
// Lazy-load route components automatically
const pages = import.meta.glob('./pages/**/*.tsx')
// { './pages/Home.tsx': () => import('./pages/Home.tsx'), ... }
Custom Plugin
import type { Plugin } from 'vite'
function svgLoader(): Plugin {
return {
name: 'svg-loader',
transform(src, id) {
if (!id.endsWith('.svg?component')) return
return {
code: `import React from 'react'; export default (props) => <svg {...props}>${src}</svg>`,
map: null,
}
},
}
}
Vitest Config
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
thresholds: { lines: 80, functions: 80, branches: 80 },
},
},
resolve: {
alias: { '@': resolve(__dirname, './src') },
},
})
Vitest Tests
// src/test/setup.ts
import '@testing-library/jest-dom'
// Component test
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { vi, describe, it, expect } from 'vitest'
describe('Button', () => {
it('calls onClick when clicked', async () => {
const user = userEvent.setup()
const
render(<Button me</Button>)
await user.click(screen.getByRole('button'))
expect(onClick).toHaveBeenCalledOnce()
})
})
Build Analysis
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
react(),
visualizer({ open: true, gzipSize: true }), // generates stats.html
],
})
Multi-Page App
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin/index.html'),
widget: resolve(__dirname, 'widget/index.html'),
},
},
},
})
Library Mode
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' },
},
},
},
})