# Vite

> Vite rules - project setup, configuration (vite.config.ts), plugins, dev server, HMR, code splitting, environment variables (import.meta.env), build optimization, import.meta.glob, TypeScript, Sass, Tailwind CSS, Vitest, SSR, workspaces

- Skill: `14bryanespinoza/vite` (Agent Skill)
- Install (CLI): `npx skillmds@latest add 14bryanespinoza/vite`
- Raw SKILL.md: https://api.skillmd.com/api/skills/14bryanespinoza/vite/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: 14BryanEspinoza (https://skillmd.com/u/14bryanespinoza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/14bryanespinoza/vite

---


# Vite — Rules and Conventions

---

## 1. Philosophy

1. **Unbundled dev** — Native ESM in browser during development. No bundle step = instant server start.
2. **ESM-first** — Code authored as ES modules. CommonJS only for legacy deps.
3. **Plugin-driven** — Core is minimal. Features via Rollup-compatible plugins.
4. **Build = Rollup** — Production build uses Rollup under the hood. Config unified.
5. **TypeScript native** — `tsc` for type-checking (separate). Vite only transpiles.

---

## 2. Minimum Versions

| Technology | Minimum Version |
| ---------- | --------------- |
| Vite       | 5.4+            |
| Node.js    | 22+             |
| pnpm       | 11+             |

---

## 3. Project Setup

### Initialize

```bash
# New project
pnpm create vite@latest my-app -- --template react-ts

# Existing project: add Vite
pnpm add -D vite @vitejs/plugin-react
```

### package.json scripts

```json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "typecheck": "tsc --noEmit"
  }
}
```

---

## 4. Configuration — `vite.config.ts`

### Essential structure

```ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  root: ".",
  base: "/",
  plugins: [react()],
  server: {
    port: 3000,
    proxy: {
      "/api": { target: "http://localhost:4000", changeOrigin: true },
    },
  },
  build: {
    outDir: "dist",
    sourcemap: true,
    minify: "esbuild",
    cssCodeSplit: true,
    assetsInlineLimit: 4096,
    rollupOptions: {
      output: {
        manualChunks: { vendor: ["react", "react-dom"] },
      },
    },
  },
  resolve: {
    alias: { "@": "/src" },
  },
  define: { __APP_VERSION__: JSON.stringify(process.env.npm_package_version) },
  optimizeDeps: { include: ["react", "react-dom"] },
});
```

### Key config rules

| Option                    | Rule                      | Why                                   |
| ------------------------- | ------------------------- | ------------------------------------- |
| `root`                    | Explicit, not implicit    | Avoids cwd confusion in monorepos     |
| `base`                    | Set for non-root deploy   | GitHub Pages, subpath hosting         |
| `plugins`                 | Official first, minimal   | Reduces maintenance surface           |
| `server.proxy`            | For API dev only          | Avoids CORS in dev                    |
| `build.minify`            | `'esbuild'` (default)     | Fastest; `'terser'` only if needed    |
| `build.cssCodeSplit`      | `true` (default)          | Enables parallel CSS loading          |
| `build.assetsInlineLimit` | `4096` (4kb)              | Inlines small assets as base64        |
| `resolve.alias`           | `@` → `/src` only         | One canonical alias                   |
| `define`                  | Only build-time constants | Replaces `process.env` in client code |
| `optimizeDeps.include`    | Heavy deps only           | Pre-bundles for faster dev start      |

---

## 5. Plugins

### Core philosophy

- **Official plugins first**: `@vitejs/plugin-react`, `@vitejs/plugin-vue`, etc.
- **Minimal set**: Each plugin adds dev/build overhead. Audit quarterly.
- **Order matters**: React plugin before other transform plugins.

### Common patterns

```ts
// vite.config.ts
import react from "@vitejs/plugin-react";
import { resolve } from "path";

export default defineConfig({
  plugins: [
    react({ include: ["**/*.tsx", "**/*.jsx"] }),
    // Conditional plugin
    process.env.ANALYZE &&
      (await import("rollup-plugin-visualizer")).visualizer(),
  ].filter(Boolean),
});
```

> For Sass/Tailwind/TypeScript integration, see respective skills: `sass`, `tailwindcss`, `typescript`.

---

## 6. Dev Server

```ts
server: {
  port: 3000,
  strictPort: true,        // Fail if port in use
  host: true,              // Listen on all interfaces
  proxy: {
    '/api': {
      target: 'http://localhost:4000',
      changeOrigin: true,
      rewrite: p => p.replace(/^\/api/, '')
    }
  },
  cors: true,
  hmr: { overlay: true }
}
```

### Rules

- **Proxy** only for `/api` or known backend paths — not `/*`
- **CORS** enabled by default; configure only if needed
- **strictPort** prevents silent port conflicts in CI

---

## 7. HMR — Hot Module Replacement

### Accept HMR in app code

```tsx
// main.tsx
if (import.meta.hot) {
  import.meta.hot.accept("./App", () => {
    // Optional: custom update logic
  });
}
```

### Rules HMR

- **Do not** manually manage HMR in library code — Vite handles it
- **CSS HMR** works automatically via `import.meta.hot.accept()` in style imports
- **Overlay** enabled by default (`hmr.overlay: true`) — shows errors in browser

---

## 8. Environment Variables — `import.meta.env`

### `.env` files (priority order)

```bash
.env                # All environments
.env.local          # Local overrides (gitignored)
.env.development    # Dev only
.env.production     # Prod only
```

### Access in code

```ts
// Only VITE_* exposed to client
const apiUrl = import.meta.env.VITE_API_URL;
const isDev = import.meta.env.DEV;
const isProd = import.meta.env.PROD;
```

### Type declarations (`vite-env.d.ts`)

```ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_FEATURE_FLAG: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}
```

### Rules Environment

- **Prefix with `VITE_`** — only these are exposed to client
- **Never commit `.env.local`** — add to `.gitignore`
- **Type `ImportMetaEnv`** in `vite-env.d.ts` for IntelliSense

---

## 9. `import.meta.glob` — Glob Imports

### Patterns

```ts
// Eager: all modules loaded immediately
const modules = import.meta.glob("./components/*.tsx", { eager: true });

// Lazy: dynamic import per module (code splitting)
const modules = import.meta.glob("./pages/**/*.tsx");

// As array
const modules = import.meta.glob("./icons/*.svg", { as: "raw", eager: true });
```

### Rules Glob Imports

- **Prefer lazy** for routes/pages — enables code splitting
- **`eager: true`** only for small, always-needed collections
- **`as: 'raw'`** for text/CSV/SVG; `as: 'url'` for assets

---

## 10. Code Splitting

### Manual chunks (vendor separation)

```ts
// vite.config.ts
build: {
  rollupOptions: {
    output: {
      manualChunks: {
        'react-vendor': ['react', 'react-dom', 'react-router-dom'],
        'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
        'utils': ['date-fns', 'zod', 'clsx']
      }
    }
  }
}
```

### Dynamic import (route-level)

```tsx
// App.tsx
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));
```

### Rules Code Splitting

- **Vendor chunks** for large, stable deps (React, UI libs)
- **Route-level lazy** for pages — reduces initial bundle
- **Avoid over-splitting** — too many chunks = more requests

---

## 11. Build Optimization

### Essential config

```ts
build: {
  target: 'es2022',           // Baseline 2023+ browsers
  minify: 'esbuild',          // Fast, good compression
  cssCodeSplit: true,         // Separate CSS files
  assetsInlineLimit: 4096,    // 4kb inline threshold
  modulePreload: { polyfill: true },
  reportCompressedSize: true,
  rollupOptions: {
    output: {
      entryFileNames: 'assets/[name]-[hash].js',
      chunkFileNames: 'assets/[name]-[hash].js',
      assetFileNames: 'assets/[name]-[hash].[ext]',
      manualChunks: { /* see §10 */ }
    }
  }
}
```

### Performance rules

| Setting                  | Value     | Why                            |
| ------------------------ | --------- | ------------------------------ |
| `target`                 | `es2022`  | Modern syntax, smaller output  |
| `minify`                 | `esbuild` | 10-20x faster than terser      |
| `cssCodeSplit`           | `true`    | Parallel CSS loading           |
| `modulePreload.polyfill` | `true`    | Preloads async chunks          |
| `reportCompressedSize`   | `true`    | Accurate gzip/brotli reporting |

> For SSR, see `deploy` skill. For workspaces/monorepos, see `package-manager` skill.

---

## 12. TypeScript

### Config separation

```json
// tsconfig.json (type-checking)
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "noEmit": true,
    "strict": true,
    "module": "ESNext",
    "moduleResolution": "bundler"
  },
  "include": ["src/**/*", "vite.config.ts"]
}
```

```json
// tsconfig.app.json (Vite build)
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "noEmit": false,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  }
}
```

### Vite + TypeScript

- **Vite transpiles only** (via esbuild) — no type checking
- **Run `tsc --noEmit` separately** — see `package-manager` scripts
- **Reference**: full TS rules in `typescript` skill

---

## 13. Methodology

Before using ANY Vite config/plugin/pattern not documented in this skill:

1. **MCP Context7** (priority): `context7_resolve-library-id` + `context7_query-docs` for Vite/plugins.
2. **Official docs**: vite.dev — verify current config options + plugin API.
3. **Project config**: `vite.config.ts`, `tsconfig.json`, `package.json` — verify against actual setup.
4. **HARD RULE**: If not in this skill AND cannot be verified against 2 authoritative sources → DO NOT USE IT. Document as assumption or risk in report to orchestrator.

---

## 14. Prohibitions

- ❌ Do not use `require()` in Vite config — ESM only (`import`)
- ❌ Do not commit `dist/` or `.vite/` — add to `.gitignore`
- ❌ Do not use `process.env` in client code — use `import.meta.env`
  with `VITE_*`
- ❌ Do not disable HMR — breaks dev experience
- ❌ Do not inline large assets (>4kb) — bloats JS bundle
- ❌ Do not use experimental plugins without fallback
- ❌ Do not mix CommonJS plugins without `legacy` option
- ❌ Do not skip `tsc --noEmit` in CI — Vite does not type-check

---

## 15. References

> **Note:** For Sass integration, see [Sass](../sass/SKILL.md)
> **Note:** For Tailwind CSS integration, see [Tailwind CSS](../tailwindcss/SKILL.md)
> **Note:** For TypeScript rules, see [TypeScript](../typescript/SKILL.md)
> **Note:** For SSR/deployment, see [Deploy](../deploy/SKILL.md)
> **Note:** For workspaces/monorepos and package manager conventions, see [Package Manager](../package-manager/SKILL.md)

---

Last updated: 2026-08

