Loading...
Loading...
Vite build tool patterns including config, plugins, HMR, env variables, proxy setup, SSR, library mode, dependency pre-bundling, and build optimization. Activate when working with vite.config.ts, Vite plugins, or Vite-based projects.
npx skill4agent add affaan-m/everything-claude-code vite-patternsvite.config.tsvite.config.js.envbuild.libnode_modules/.viteVITE_// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname },
},
})// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd()) // VITE_ prefixed only (safe)
return {
plugins: [react()],
server: command === 'serve' ? { port: 3000 } : undefined,
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})| Key | Default | Description |
|---|---|---|
| | Project root (where |
| | Public base path for deployed assets |
| | Prefix for client-exposed env vars |
| | Output directory |
| | Minifier ( |
| | |
| Plugin | Purpose | When to use |
|---|---|---|
| React HMR + Fast Refresh via SWC | Default for React apps (faster than Babel variant) |
| React HMR + Fast Refresh via Babel | Only if you need Babel plugins (emotion, MobX decorators) |
| Vue 3 SFC support | Vue apps |
| Runs | Any TypeScript app — Vite does NOT type-check during |
| Honors | Any time you already have aliases in |
| Emits | Publishing TypeScript libraries |
| Imports SVGs as React components | React apps using SVGs as components |
| Bundle treemap/sunburst report | Periodic bundle size audits (use |
| Zero-config PWA + Workbox | Offline-capable apps |
vite buildvite-plugin-checkertsc --noEmitvite.config.ts// vite.config.ts — minimal inline plugin
function myPlugin(): Plugin {
return {
name: 'my-plugin', // required, must be unique
enforce: 'pre', // 'pre' | 'post' (optional)
apply: 'build', // 'build' | 'serve' (optional)
transform(code, id) {
if (!id.endsWith('.custom')) return
return { code: transformCustom(code), map: null }
},
}
}transformresolveIdloadtransformIndexHtmlconfigureServerhotUpdatehandleHotUpdate\0resolveId'\0virtual:my-id''virtual:my-id'vite-plugin-inspect@vitejs/plugin-react@vitejs/plugin-vueimport.meta.hot// src/store.ts — manual HMR for a vanilla module
if (import.meta.hot) {
// Persist state across updates (must MUTATE, never reassign .data)
import.meta.hot.data.count = import.meta.hot.data.count ?? 0
// Cleanup side effects before module is replaced
import.meta.hot.dispose((data) => clearInterval(data.intervalId))
// Accept this module's own updates
import.meta.hot.accept()
}import.meta.hot.env.env.local.env.[mode].env.[mode].local*.localVITE_import.meta.env.VITE_API_URL // string
import.meta.env.MODE // 'development' | 'production' | custom
import.meta.env.BASE_URL // base config value
import.meta.env.DEV // boolean
import.meta.env.PROD // boolean
import.meta.env.SSR // boolean// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()) // VITE_ prefixed only (safe)
return {
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})VITE_VITE_VITE_VITE_loadEnv('')// BAD: passing '' as the third arg loads ALL env vars — including server secrets —
// and makes them available to inline into client code via `define`.
const env = loadEnv(mode, process.cwd(), '')
// GOOD: explicit prefix list
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])build: {
sourcemap: false, // default — keep it this way
}.gitignore.env.local.env.*.localdist/node_modules/.vite// vite.config.ts — server.proxy
server: {
proxy: {
'/foo': 'http://localhost:4567', // string shorthand
'/api': {
target: 'http://localhost:8080',
changeOrigin: true, // needed for virtual-hosted backends
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
}ws: true// vite.config.ts — build.rolldownOptions
build: {
rolldownOptions: {
output: {
// Object form: group specific packages
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
},
},
},
}// Function form: split by heuristic
manualChunks(id) {
if (id.includes('node_modules/react')) return 'react-vendor'
if (id.includes('node_modules')) return 'vendor'
}index.ts// BAD — importing one util forces Vite to load the whole barrel
import { slash } from '@/utils'
// GOOD — direct import, only the one file is loaded
import { slash } from '@/utils/slash'resolve.extensions// BAD
import Component from './Component'
// GOOD
import Component from './Component.tsx'tsconfig.jsonallowImportingTsExtensionsresolve.extensionsserver.warmup.clientFiles// vite.config.ts
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
},
}vite devvite --profilep+enter.cpuprofilebuildStartconfigconfigResolvedbuild.libvite-plugin-dtstsc --emitDeclarationOnly// vite.config.ts
build: {
lib: {
entry: 'src/index.ts',
formats: ['es', 'cjs'],
fileName: (format) => `my-lib.${format}.js`,
},
rolldownOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'], // every peer dep
},
}createServer({ middlewareMode: true })// vite.config.ts — ssr options
ssr: {
external: ['node-native-package'], // keep as require() in SSR bundle
noExternal: ['esm-only-package'], // force-bundle into SSR output (fixes most SSR errors)
target: 'node', // 'node' or 'webworker'
}// vite.config.ts — optimizeDeps
optimizeDeps: {
include: [
'lodash-es', // force pre-bundle known heavy deps
'cjs-package', // CJS deps that cause interop issues
'deep-lib/components/**', // glob for deep imports
],
exclude: ['local-esm-package'], // must be valid ESM if excluded
force: true, // ignore cache, re-optimize (temporary debugging)
}vite build && vite previewdist/assets/localhost// vite.config.ts — Docker/container setup
server: {
host: true, // bind 0.0.0.0
hmr: { clientPort: 3000 }, // if behind a reverse proxy
}// vite.config.ts — monorepo file access
server: {
fs: {
allow: ['..'], // allow parent directory (workspace root)
},
}// BAD: Setting envPrefix to '' exposes ALL env vars (including secrets) to the client
envPrefix: ''
// BAD: Assuming require() works in application source code — Vite is ESM-first
const lib = require('some-lib') // use import instead
// BAD: Splitting every node_module into its own chunk — creates hundreds of tiny files
manualChunks(id) {
if (id.includes('node_modules')) {
return id.split('node_modules/')[1].split('/')[0] // one chunk per package
}
}
// BAD: Not externalizing peer deps in library mode — causes duplicate runtime errors
// build.lib without rolldownOptions.external
// BAD: Using deprecated esbuild minifier
build: { minify: 'esbuild' } // use 'oxc' (default) or 'terser'
// BAD: Mutating import.meta.hot.data by reassignment
import.meta.hot.data = { count: 0 } // WRONG: must mutate properties, not reassign
import.meta.hot.data.count = 0 // CORRECTvite previewdist/vite buildvite-plugin-checkertsc --noEmit@vitejs/plugin-legacyresolve.aliastsconfig.jsonvite-tsconfig-pathsnode_modules/.vite| Pattern | When to Use |
|---|---|
| Always — provides type inference |
| Access env vars in config (explicit prefix) |
| Any TypeScript app (fills the type-check gap) |
| Instead of hand-rolled |
| CJS deps causing interop issues |
| Route API requests to backend in dev |
| Docker, containers, remote access |
| Pre-transform hot-path routes |
| Publishing npm packages |
| Vendor bundle splitting |
| Debug slow dev server |
| Smoke-test prod bundle locally (NOT a prod server) |
frontend-patternsdocker-patternsnextjs-turbopack