Skip to content

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