Skip to main content
Technology & EngineeringBuild Tools161 lines

Vite

Vite configuration, plugins, and dev server optimization for modern JavaScript projects

Quick Summary30 lines
You are an expert in Vite for JavaScript project bundling, dev server configuration, and plugin authoring.

## Key Points

- Use `defineConfig` for type-safe configuration and IDE autocompletion.
- Split vendor chunks with `manualChunks` to improve caching on production deployments.
- Keep `server.proxy` settings for local API development to avoid CORS issues.
- Use dynamic `import()` for route-level code splitting; Vite handles this natively.
- Prefer `import.meta.glob` for bulk file imports rather than manual require contexts.
- Set `build.target` to match your browser support requirements to control output syntax.
- **Missing VITE_ prefix**: Environment variables without the `VITE_` prefix are not exposed to client bundles. This is a security feature, not a bug.
- **CJS dependencies in dev**: Some CommonJS-only packages cause issues with Vite's ESM dev server. Add them to `optimizeDeps.include` to pre-bundle them.
- **HMR boundary issues**: If HMR stops working, ensure components are exported as named or default exports from their own files; side effects in module scope can break HMR.
- **Public directory confusion**: Files in `public/` are served at root and copied as-is. Files in `src/assets/` are processed by the bundler. Mixing these up leads to broken asset references.
- **SSR gotchas**: When using Vite for SSR, ensure Node-only packages are listed in `ssr.noExternal` or `ssr.external` as needed.

## Quick Example

```bash
npm create vite@latest my-app -- --template react-ts
cd my-app && npm install
```

```bash
# .env.development
VITE_API_URL=http://localhost:8080
VITE_APP_TITLE=My App
```
skilldb get build-tools-skills/ViteFull skill: 161 lines
Paste into your CLAUDE.md or agent config

Vite — Build Tools

You are an expert in Vite for JavaScript project bundling, dev server configuration, and plugin authoring.

Core Philosophy

Overview

Vite is a next-generation frontend build tool that leverages native ES modules for an extremely fast dev server and uses Rollup under the hood for optimized production builds. It supports TypeScript, JSX, CSS modules, and asset handling out of the box with minimal configuration.

Setup & Configuration

Basic Project Setup

npm create vite@latest my-app -- --template react-ts
cd my-app && npm install

vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/api/, ''),
      },
    },
  },
  build: {
    target: 'esnext',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
        },
      },
    },
  },
});

Environment Variables

# .env.development
VITE_API_URL=http://localhost:8080
VITE_APP_TITLE=My App

Access via import.meta.env.VITE_API_URL. Only variables prefixed with VITE_ are exposed to client code.

Core Patterns

Custom Plugin

function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    transformIndexHtml(html) {
      return html.replace(
        '<!-- inject -->',
        `<script>window.__BUILD_TIME__="${new Date().toISOString()}"</script>`
      );
    },
    configureServer(server) {
      server.middlewares.use('/health', (req, res) => {
        res.end('ok');
      });
    },
  };
}

Library Mode

export default defineConfig({
  build: {
    lib: {
      entry: path.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' },
      },
    },
  },
});

CSS Configuration

export default defineConfig({
  css: {
    modules: {
      localsConvention: 'camelCaseOnly',
    },
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables" as *;`,
      },
    },
  },
});

Best Practices

  • Use defineConfig for type-safe configuration and IDE autocompletion.
  • Split vendor chunks with manualChunks to improve caching on production deployments.
  • Keep server.proxy settings for local API development to avoid CORS issues.
  • Use dynamic import() for route-level code splitting; Vite handles this natively.
  • Prefer import.meta.glob for bulk file imports rather than manual require contexts.
  • Set build.target to match your browser support requirements to control output syntax.

Common Pitfalls

  • Missing VITE_ prefix: Environment variables without the VITE_ prefix are not exposed to client bundles. This is a security feature, not a bug.
  • CJS dependencies in dev: Some CommonJS-only packages cause issues with Vite's ESM dev server. Add them to optimizeDeps.include to pre-bundle them.
  • HMR boundary issues: If HMR stops working, ensure components are exported as named or default exports from their own files; side effects in module scope can break HMR.
  • Public directory confusion: Files in public/ are served at root and copied as-is. Files in src/assets/ are processed by the bundler. Mixing these up leads to broken asset references.
  • SSR gotchas: When using Vite for SSR, ensure Node-only packages are listed in ssr.noExternal or ssr.external as needed.

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add build-tools-skills

Get CLI access →