Research-mind toolchains-javascript-build-vite

Vite Build Tool Skill

install
source · Clone the upstream repo
git clone https://github.com/MacPhobos/research-mind
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/MacPhobos/research-mind "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/toolchains-javascript-build-vite" ~/.claude/skills/macphobos-research-mind-toolchains-javascript-build-vite && rm -rf "$T"
manifest: .claude/skills/toolchains-javascript-build-vite/skill.md
source content

Vite Build Tool Skill


progressive_disclosure: entry_point: summary: "Lightning-fast build tool with instant HMR and ESM-first architecture for modern web development" when_to_use: - "When building React/Vue/Svelte/Preact applications" - "When needing instant HMR (Hot Module Replacement)" - "When migrating from webpack/CRA/Parcel" - "When setting up TypeScript projects with zero config" - "When building component libraries or UI frameworks" quick_start: - "npm create vite@latest my-app" - "Select framework: React/Vue/Svelte/Vanilla" - "cd my-app && npm install && npm run dev" essential_commands: - "vite: Start dev server with instant HMR" - "vite build: Production build with Rollup" - "vite preview: Preview production build locally" token_estimate: entry: 75 full: 4000

Core Concepts

Why Vite?

ESM-First Architecture

  • No bundling in development - serves native ESM
  • Instant cold server start (no matter project size)
  • Lightning-fast HMR that stays fast as app grows
  • Rollup-based production builds with optimal code splitting

Key Advantages

  • Dev Speed: 10-100x faster than webpack (no bundling)
  • Zero Config: TypeScript, JSX, CSS modules out-of-box
  • Framework Agnostic: React, Vue, Svelte, Preact, Lit
  • Plugin Ecosystem: Rollup plugins + Vite-specific plugins
  • Modern by Default: ESM, dynamic imports, top-level await

Quick Start

Create New Project

# Interactive creation
npm create vite@latest

# With template
npm create vite@latest my-app -- --template react-ts
npm create vite@latest my-app -- --template vue
npm create vite@latest my-app -- --template svelte-ts

# Available templates:
# vanilla, vanilla-ts
# react, react-ts, react-swc, react-swc-ts
# vue, vue-ts
# svelte, svelte-ts
# preact, preact-ts
# lit, lit-ts

Essential Commands

# Development server (instant start)
npm run dev
vite --port 3000 --host 0.0.0.0

# Production build
npm run build
vite build --mode production

# Preview production build
npm run preview
vite preview --port 8080

# Clear cache (dependency pre-bundling)
rm -rf node_modules/.vite

Configuration

Basic vite.config.ts

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

export default defineConfig({
  plugins: [react()],

  // Dev server
  server: {
    port: 3000,
    open: true,
    cors: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  },

  // Build options
  build: {
    outDir: 'dist',
    sourcemap: true,
    minify: 'terser',
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash-es', 'date-fns']
        }
      }
    }
  },

  // Path aliases
  resolve: {
    alias: {
      '@': '/src',
      '@components': '/src/components',
      '@utils': '/src/utils'
    }
  }
});

Framework-Specific Configs

React + TypeScript + SWC

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

export default defineConfig({
  plugins: [
    react({
      // Enable Fast Refresh
      fastRefresh: true,
      // SWC optimizations
      tsDecorators: true
    })
  ],

  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  },

  build: {
    target: 'es2020',
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('react') || id.includes('react-dom')) {
              return 'react-vendor';
            }
            return 'vendor';
          }
        }
      }
    }
  }
});

Vue 3 + TypeScript

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

export default defineConfig({
  plugins: [
    vue(),
    vueJsx() // For JSX/TSX in Vue
  ],

  resolve: {
    alias: {
      '@': '/src',
      'vue': 'vue/dist/vue.esm-bundler.js'
    }
  },

  // Vue-specific optimizations
  optimizeDeps: {
    include: ['vue', 'vue-router', 'pinia']
  }
});

Svelte + TypeScript

import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [
    svelte({
      compilerOptions: {
        dev: process.env.NODE_ENV !== 'production'
      },
      hot: {
        preserveLocalState: true
      }
    })
  ],

  build: {
    target: 'es2020',
    minify: 'esbuild'
  }
});

Environment Variables

.env Files

# .env (loaded in all cases)
VITE_APP_TITLE=My App

# .env.local (local overrides, gitignored)
VITE_API_KEY=secret-key

# .env.development
VITE_API_URL=http://localhost:8000

# .env.production
VITE_API_URL=https://api.production.com

Usage in Code

// TypeScript: Define env types
interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string;
  readonly VITE_API_URL: string;
  readonly VITE_API_KEY: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

// Access variables (MUST start with VITE_)
console.log(import.meta.env.VITE_API_URL);
console.log(import.meta.env.MODE); // 'development' or 'production'
console.log(import.meta.env.DEV); // boolean
console.log(import.meta.env.PROD); // boolean

vite-env.d.ts

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

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string;
  readonly VITE_API_URL: string;
  readonly VITE_API_KEY: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

Plugin Ecosystem

Official Plugins

# React
npm install -D @vitejs/plugin-react          # Babel-based
npm install -D @vitejs/plugin-react-swc      # SWC-based (faster)

# Vue
npm install -D @vitejs/plugin-vue
npm install -D @vitejs/plugin-vue-jsx

# Svelte
npm install -D @sveltejs/vite-plugin-svelte

# Legacy browser support
npm install -D @vitejs/plugin-legacy

Essential Community Plugins

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import tsconfigPaths from 'vite-tsconfig-paths';
import { VitePWA } from 'vite-plugin-pwa';
import compression from 'vite-plugin-compression';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    react(),

    // TypeScript paths from tsconfig.json
    tsconfigPaths(),

    // PWA support
    VitePWA({
      registerType: 'autoUpdate',
      manifest: {
        name: 'My App',
        short_name: 'App',
        theme_color: '#ffffff'
      }
    }),

    // Gzip/Brotli compression
    compression({
      algorithm: 'brotliCompress',
      ext: '.br'
    }),

    // Bundle size visualization
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true
    })
  ]
});

TypeScript Configuration

tsconfig.json for Vite

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    /* Path aliases */
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"]
    }
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

tsconfig.node.json (for config files)

{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

CSS & Asset Handling

CSS Modules

// Component.module.css
.button {
  background: blue;
  color: white;
}

// Component.tsx
import styles from './Component.module.css';

export function Button() {
  return <button className={styles.button}>Click</button>;
}

PostCSS Configuration

// postcss.config.js
export default {
  plugins: {
    'postcss-nesting': {},
    'autoprefixer': {},
    'cssnano': {
      preset: 'default'
    }
  }
};

Sass/SCSS

npm install -D sass
// Just import - Vite handles it
import './styles.scss';

// Vite config for global variables
export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`
      }
    }
  }
});

Static Assets

// Import as URL
import imgUrl from './assets/logo.png';
console.log(imgUrl); // '/assets/logo.abc123.png'

// Import as string (raw)
import svgRaw from './icon.svg?raw';
console.log(svgRaw); // '<svg>...</svg>'

// Import as Web Worker
import Worker from './worker?worker';
const worker = new Worker();

// Public directory (not processed)
// public/favicon.ico → /favicon.ico
<img src="/favicon.ico" />

Build Optimization

Code Splitting

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        // Manual chunks for better caching
        manualChunks(id) {
          if (id.includes('node_modules')) {
            // Split by package
            if (id.includes('react') || id.includes('react-dom')) {
              return 'react-vendor';
            }
            if (id.includes('@mui')) {
              return 'mui-vendor';
            }
            if (id.includes('lodash')) {
              return 'lodash-vendor';
            }
            return 'vendor';
          }
        },

        // Naming patterns
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
      }
    },

    // Chunk size warnings
    chunkSizeWarningLimit: 1000,

    // Minification
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
});

Dependency Pre-bundling

export default defineConfig({
  optimizeDeps: {
    // Force include (for dynamic imports)
    include: [
      'react',
      'react-dom',
      'lodash-es'
    ],

    // Force exclude (keep unbundled)
    exclude: [
      'your-local-package'
    ],

    // esbuild options
    esbuildOptions: {
      target: 'es2020',
      define: {
        global: 'globalThis'
      }
    }
  }
});

Library Mode

// Build as library/package
import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      name: 'MyLibrary',
      fileName: (format) => `my-library.${format}.js`,
      formats: ['es', 'cjs', 'umd']
    },

    rollupOptions: {
      // Externalize deps that shouldn't be bundled
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM'
        }
      }
    }
  }
});

Migration Guides

From Create React App (CRA)

# 1. Remove CRA
npm uninstall react-scripts

# 2. Install Vite
npm install -D vite @vitejs/plugin-react

# 3. Update package.json scripts
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}
// 4. Create vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000
  },
  build: {
    outDir: 'build' // CRA uses 'build'
  }
});
<!-- 5. Update index.html (move to root, remove %PUBLIC_URL%) -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
// 6. Update environment variables
// CRA: REACT_APP_API_URL
// Vite: VITE_API_URL

// Before (CRA)
process.env.REACT_APP_API_URL

// After (Vite)
import.meta.env.VITE_API_URL

From Webpack

// webpack.config.js → vite.config.ts

// Webpack concepts → Vite equivalents:
// - entry → index.html + <script src="main.tsx">
// - output.path → build.outDir
// - resolve.alias → resolve.alias (same!)
// - module.rules → plugins (for file types)
// - optimization.splitChunks → build.rollupOptions.output.manualChunks
// - DefinePlugin → define config option
// - devServer → server config

SSR (Server-Side Rendering)

Basic SSR Setup

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    // SSR build
    ssr: true,
    outDir: 'dist/server'
  }
});
// server.ts (Express + Vite SSR)
import express from 'express';
import { createServer as createViteServer } from 'vite';

async function createServer() {
  const app = express();

  const vite = await createViteServer({
    server: { middlewareMode: true },
    appType: 'custom'
  });

  app.use(vite.middlewares);

  app.use('*', async (req, res) => {
    const url = req.originalUrl;

    try {
      // Load template
      let template = await vite.transformIndexHtml(
        url,
        fs.readFileSync('index.html', 'utf-8')
      );

      // Load SSR entry
      const { render } = await vite.ssrLoadModule('/src/entry-server.tsx');

      // Render app
      const appHtml = await render(url);

      // Inject into template
      const html = template.replace(`<!--ssr-outlet-->`, appHtml);

      res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
    } catch (e) {
      vite.ssrFixStacktrace(e);
      res.status(500).end(e.stack);
    }
  });

  app.listen(3000);
}

createServer();

Performance Best Practices

Dev Server Optimization

export default defineConfig({
  server: {
    // Faster dependency resolution
    fs: {
      strict: false,
      allow: ['..']
    },

    // WebSocket connection
    hmr: {
      overlay: true,
      clientPort: 3000
    }
  },

  // Faster pre-bundling
  optimizeDeps: {
    force: false, // Only rebuild on deps change
    include: ['large-dependencies']
  },

  // Faster transforms
  esbuild: {
    logOverride: { 'this-is-undefined-in-esm': 'silent' }
  }
});

Build Performance

export default defineConfig({
  build: {
    // Faster builds (esbuild vs terser)
    minify: 'esbuild',

    // Disable source maps in production
    sourcemap: false,

    // Target modern browsers only
    target: 'es2020',

    // Reduce rollup overhead
    rollupOptions: {
      cache: true
    },

    // Report compressed size (slower but informative)
    reportCompressedSize: false
  }
});

Smart Code Splitting

// Dynamic imports for route-based splitting
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

// Component-level splitting
const HeavyChart = lazy(() => import('./components/HeavyChart'));

// Conditional loading
const AdminPanel = lazy(() =>
  import(/* @vite-ignore */ './admin/Panel')
);

Debugging

Common Issues

Issue: Module not found

# Clear dependency cache
rm -rf node_modules/.vite
npm install

Issue: HMR not working

// Check vite.config.ts
export default defineConfig({
  server: {
    hmr: {
      overlay: true
    },
    watch: {
      usePolling: true // For Docker/WSL
    }
  }
});

Issue: Build errors but dev works

# Check for:
# - TypeScript errors (tsc --noEmit)
# - Import paths (case sensitivity)
# - Missing dependencies in package.json

Debug Mode

# Verbose logging
DEBUG=vite:* vite

# Specific debug scopes
DEBUG=vite:deps vite
DEBUG=vite:hmr vite
DEBUG=vite:config vite

Advanced Patterns

Multi-page App

import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        admin: resolve(__dirname, 'admin/index.html'),
        docs: resolve(__dirname, 'docs/index.html')
      }
    }
  }
});

Custom Plugin

import { Plugin } from 'vite';

export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',

    // Transform code
    transform(code, id) {
      if (id.endsWith('.custom')) {
        return {
          code: transformCode(code),
          map: null
        };
      }
    },

    // Handle HMR
    handleHotUpdate({ file, server }) {
      if (file.endsWith('.custom')) {
        server.ws.send({
          type: 'custom',
          event: 'update'
        });
      }
    },

    // Modify config
    config(config, env) {
      return {
        define: {
          __BUILD_TIME__: JSON.stringify(new Date().toISOString())
        }
      };
    }
  };
}

Monorepo Support

// packages/app/vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  resolve: {
    alias: {
      '@shared': '../../packages/shared/src'
    }
  },

  optimizeDeps: {
    include: [
      '@workspace/shared' // Pre-bundle workspace deps
    ]
  },

  server: {
    fs: {
      allow: ['../..'] // Allow serving from monorepo root
    }
  }
});

Integration Examples

Vite + React + TypeScript + Tailwind

npm create vite@latest my-app -- --template react-ts
cd my-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Vite + Testing (Vitest)

npm install -D vitest @testing-library/react jsdom
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html']
    }
  }
});

Quick Reference

Essential Configuration Options

export default defineConfig({
  // Base public path
  base: '/my-app/',

  // Modes: 'development' | 'production'
  mode: 'production',

  // Define global constants
  define: {
    __APP_VERSION__: JSON.stringify('1.0.0')
  },

  // Public directory
  publicDir: 'public',

  // Cache directory
  cacheDir: 'node_modules/.vite',

  // Log level
  logLevel: 'info' // 'info' | 'warn' | 'error' | 'silent'
});

Common Plugin Combinations

// React + TypeScript + PWA + Analytics
import react from '@vitejs/plugin-react-swc';
import { VitePWA } from 'vite-plugin-pwa';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  plugins: [
    react(),
    tsconfigPaths(),
    VitePWA({
      registerType: 'autoUpdate'
    })
  ]
});

Related Skills

When using Vite, consider these complementary skills (available in the skill library):

  • React: React framework integration - use Vite's optimized React plugin for fast development
  • Vue: Vue framework integration - leverage Vite's native Vue support and SFC compilation
  • Svelte: Svelte framework integration - combine Svelte's compile-time optimizations with Vite's speed
  • TypeScript: TypeScript configuration - configure TypeScript with Vite for type-safe development
  • Vitest: Testing with Vite - use Vitest for lightning-fast unit testing with Vite's transform pipeline

Resources

Official Documentation

Community

Migration Tools