Vite 6 Reference: Config, Environment Variables, Assets, Library Mode & Plugins
Vite 6 is the standard build tool for modern frontend projects. Dev server uses native ES modules — no bundling during development, so cold start is instant regardless of project size. Production builds use Rollup. The key thing to understand: Vite has two separate modes (dev with esbuild/ESM, build with Rollup), so occasionally something that works in dev fails in production. Always test your production build before shipping.
1. Config — vite.config.ts
defineConfig, plugins, resolve aliases, environment variables, and server proxy
// vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [
react(), // JSX transform + Fast Refresh
// Other common plugins:
// tsconfigPaths() — @vitejs/plugin-tsconfig-paths
// svgr() — @svgr/vite (import SVGs as React components)
// pwa() — vite-plugin-pwa
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@components": path.resolve(__dirname, "./src/components"),
},
},
server: {
port: 3000,
open: true,
// Proxy API calls to avoid CORS in dev:
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
// HMR over network (not just localhost):
hmr: { host: "0.0.0.0" },
},
build: {
outDir: "dist",
sourcemap: true,
// Rollup options — split vendor chunks for better caching:
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
router: ["react-router-dom"],
},
},
},
},
// CSS preprocessors (install sass/less/stylus separately):
css: {
preprocessorOptions: {
scss: { additionalData: '@use "@/styles/variables.scss" as *;' },
},
},
});
2. Environment Variables
VITE_ prefix, .env files, import.meta.env, TypeScript types, and secrets
// VITE_ prefix = exposed to client code. No prefix = server/build only.
// Never put secrets in VITE_ vars — they're bundled into the JS
// .env files (priority: .env.local > .env.[mode] > .env):
// .env — all modes
// .env.local — all modes, git-ignored (personal overrides)
// .env.development — dev only (vite dev)
// .env.production — prod only (vite build)
// .env.test — test only (vitest)
// .env.development:
VITE_API_URL=http://localhost:8080
VITE_ENABLE_ANALYTICS=false
VITE_APP_NAME=My App
// .env.production:
VITE_API_URL=https://api.example.com
VITE_ENABLE_ANALYTICS=true
// Access in code:
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" | "test"
const base = import.meta.env.BASE_URL; // from vite.config base option
// TypeScript types for env vars (src/vite-env.d.ts):
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
readonly VITE_ENABLE_ANALYTICS: string; // always string, check/convert
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
// Use --mode to load different .env files:
// vite build --mode staging → loads .env.staging
3. Static Assets, CSS & HMR
Asset imports, ?url and ?raw suffixes, CSS Modules, global styles, and HMR API
// Asset imports — Vite handles these automatically:
import logo from "./assets/logo.png"; // → URL string in prod
import icon from "./assets/icon.svg"; // → URL string
import iconRaw from "./assets/icon.svg?raw"; // → SVG string (inline)
import workerUrl from "./worker.ts?url"; // → URL for Web Worker
import wasmUrl from "./module.wasm?url"; // → URL for WASM
// In component:
<img src={logo} alt="Logo" />
// CSS Modules (scoped class names):
import styles from "./Button.module.css";
// styles.button → "Button_button__xK3sD" (hashed class name)
<button className={styles.button}>Click</button>
// Global CSS — import in main.tsx:
import "./styles/globals.css";
import "./styles/variables.css";
// Tailwind CSS (install tailwindcss + @tailwindcss/vite):
// vite.config.ts: plugins: [tailwindcss()]
// app.css: @import "tailwindcss"
// Fast Refresh (HMR) boundary rules:
// Components with exports only → auto HMR
// Files that export non-components → HMR not scoped, full reload
// To mark explicit HMR boundary:
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// Handle module update
});
import.meta.hot.dispose(() => {
// Cleanup before old module is replaced
});
}
4. Multi-Page App, Library Mode & Preview
Multiple entry points, build as a library, vite preview, and base path
// Multi-page application (each HTML file = separate entry point):
export default defineConfig({
build: {
rollupOptions: {
input: {
main: path.resolve(__dirname, "index.html"),
admin: path.resolve(__dirname, "admin/index.html"),
embed: path.resolve(__dirname, "embed/index.html"),
},
},
},
});
// Library mode — build a distributable npm package:
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "MyLib",
fileName: (format) => `my-lib.${format}.js`,
formats: ["es", "cjs"], // or ["umd"] for browser globals
},
rollupOptions: {
external: ["react", "react-dom"], // don't bundle peer deps
output: {
globals: { react: "React", "react-dom": "ReactDOM" },
},
},
},
});
// vite preview — serve production build locally (before deploying):
// npm run build && npx vite preview
// Useful to catch dev/prod discrepancies
// Base path — for non-root deployment (GitHub Pages, subdirectory):
export default defineConfig({
base: "/my-app/", // all asset URLs prefixed with /my-app/
// Or use env: base: process.env.PUBLIC_URL ?? "/",
});
// Docker — serve built dist with nginx:
// COPY dist /usr/share/nginx/html
// COPY nginx.conf /etc/nginx/conf.d/default.conf
// nginx.conf: try_files $uri /index.html ← SPA routing
5. Performance, Code Splitting & Plugin API
Dynamic imports, chunk analysis, build stats, and writing a Vite plugin
// Dynamic imports — automatic code splitting:
const LazyComponent = React.lazy(() => import("./pages/Dashboard"));
// Vite creates a separate chunk for Dashboard automatically
// Prefetch a chunk:
const load = () => import(/* @vite-ignore */ "./pages/Settings");
// Hint to browser: <link rel="prefetch" href="/assets/Settings-xyz.js">
// Chunk size analysis:
// npm install -D rollup-plugin-visualizer
import { visualizer } from "rollup-plugin-visualizer";
plugins: [visualizer({ open: true, gzipSize: true })]
// Opens treemap in browser after build
// Suppress large chunk warning:
build: {
chunkSizeWarningLimit: 1000, // KB (default 500)
}
// Vite Plugin API — transform files during build:
import type { Plugin } from "vite";
function myPlugin(): Plugin {
return {
name: "my-plugin",
// Transform source code:
transform(code, id) {
if (!id.endsWith(".ts")) return;
return code.replace("__VERSION__", JSON.stringify("1.0.0"));
},
// Serve custom files in dev:
configureServer(server) {
server.middlewares.use("/custom", (req, res) => {
res.end("Custom response");
});
},
// Inject into HTML:
transformIndexHtml(html) {
return html.replace("</head>", "<script>window.ENV='prod'</script></head>");
},
};
}
Track Node.js and build tooling releases at ReleaseRun. Related: TypeScript Reference | Vitest Reference | React Reference
🔍 Free tool: npm Package Health Checker — check Vite and its plugins for known CVEs and active maintenance before updating your build config.
Founded
2023 in London, UK
Contact
hello@releaserun.com