vite

Platform: Web only. Mobile demos use Expo with Metro bundler. See the expo-sdk skill.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "vite" with this command: npx skills add nguyenhuuca/assessment/nguyenhuuca-assessment-vite

Vite

Platform: Web only. Mobile demos use Expo with Metro bundler. See the expo-sdk skill.

Overview

Build tool and development server patterns for Vite 7.x. Provides instant server start, lightning-fast HMR, optimized production builds, and extensive plugin ecosystem with first-class TypeScript support.

Install: pnpm add -D vite

Workflows

Initial setup:

  • Create vite.config.ts with TypeScript types

  • Install React plugin: pnpm add -D @vitejs/plugin-react

  • Configure path aliases for clean imports

  • Set up environment variables with .env files

  • Test dev server: pnpm vite

Production optimization:

  • Configure build output directory and asset handling

  • Set up code splitting and chunk optimization

  • Enable build compression (gzip/brotli)

  • Configure minification options

  • Run production build: pnpm vite build

  • Preview build locally: pnpm vite preview

Basic Configuration

Minimal vite.config.ts

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

export default defineConfig({ plugins: [react()], server: { port: 5173, open: true }, build: { outDir: 'dist' } });

TypeScript-Aware Config

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

export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src'), '@components': path.resolve(__dirname, './src/components'), '@hooks': path.resolve(__dirname, './src/hooks'), '@utils': path.resolve(__dirname, './src/utils'), '@types': path.resolve(__dirname, './src/types') } } });

Update tsconfig.json paths to match:

{ "compilerOptions": { "baseUrl": ".", "paths": { "@/": ["./src/"], "@components/": ["./src/components/"], "@hooks/": ["./src/hooks/"], "@utils/": ["./src/utils/"], "@types/": ["./src/types/"] } } }

React Plugin Setup

Basic React Plugin

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

export default defineConfig({ plugins: [ react({ // Babel plugins for React (optional) babel: { plugins: [ // Add custom babel plugins here ] } }) ] });

Note: Fast Refresh is enabled by default in @vitejs/plugin-react . No configuration needed.

React with SWC (Faster Alternative)

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

export default defineConfig({ plugins: [ react({ // SWC plugins plugins: [ // Add SWC plugins here ] }) ] });

Environment Variables

.env File Structure

.env - Base config (committed)

VITE_APP_NAME=Demo Harness VITE_API_VERSION=v1

.env.local - Local overrides (gitignored)

VITE_API_URL=http://localhost:3000

.env.development - Dev defaults

VITE_DEBUG=true VITE_API_URL=http://dev.example.com

.env.production - Production defaults

VITE_DEBUG=false VITE_API_URL=https://api.example.com

CRITICAL: All env vars must start with VITE_ to be exposed to client code.

Using Environment Variables

// ✅ Accessing env vars in code const apiUrl = import.meta.env.VITE_API_URL; const isDev = import.meta.env.DEV; const isProd = import.meta.env.PROD; const mode = import.meta.env.MODE; // 'development' | 'production'

// Type-safe env vars interface ImportMetaEnv { readonly VITE_APP_NAME: string; readonly VITE_API_URL: string; readonly VITE_API_VERSION: string; readonly VITE_DEBUG: string; }

interface ImportMeta { readonly env: ImportMetaEnv; }

// ❌ NEVER commit secrets to .env files // Use .env.local for API keys and credentials

Configuring Environment Variables

import { defineConfig, loadEnv } from 'vite';

export default defineConfig(({ mode }) => { // Load env file based on mode const env = loadEnv(mode, process.cwd(), '');

return { define: { // Expose non-VITE_ prefixed vars APP_VERSION: JSON.stringify(env.npm_package_version) }, server: { port: Number(env.PORT) || 5173 } }; });

Development Server

Basic Server Configuration

export default defineConfig({ server: { port: 5173, strictPort: true, // Exit if port is already in use open: true, // Open browser on server start cors: true, // Enable CORS

// Hot Module Replacement
hmr: {
  overlay: true // Show error overlay
},

// File watching
watch: {
  // Ignore dotfiles
  ignored: ['**/.*']
}

} });

Proxy Configuration for API

export default defineConfig({ server: { proxy: { // Proxy API requests to backend '/api': { target: 'http://localhost:3000', changeOrigin: true, rewrite: (path) => path.replace(/^/api/, '') },

  // WebSocket proxy
  '/ws': {
    target: 'ws://localhost:3000',
    ws: true
  },

  // Multiple backends
  '/v1': {
    target: 'http://localhost:3001',
    changeOrigin: true
  },
  '/v2': {
    target: 'http://localhost:3002',
    changeOrigin: true
  }
}

} });

HTTPS Development Server

import { defineConfig } from 'vite'; import fs from 'node:fs';

export default defineConfig({ server: { https: { key: fs.readFileSync('./.cert/key.pem'), cert: fs.readFileSync('./.cert/cert.pem') } } });

Build Optimization

Code Splitting and Chunking

export default defineConfig({ build: { rollupOptions: { output: { // Manual chunk splitting manualChunks: { // Vendor chunks 'react-vendor': ['react', 'react-dom'], 'router-vendor': ['react-router-dom'], 'animation-vendor': ['framer-motion'],

      // Feature-based chunks
      'dashboard': ['./src/components/views/DashboardView.tsx'],
      'reports': ['./src/components/views/ReportsView.tsx']
    },

    // Asset file naming
    assetFileNames: 'assets/[name]-[hash][extname]',
    chunkFileNames: 'js/[name]-[hash].js',
    entryFileNames: 'js/[name]-[hash].js'
  }
},

// Chunk size warnings
chunkSizeWarningLimit: 500, // KB

// Minification
minify: 'esbuild', // 'terser' | 'esbuild'

// Source maps
sourcemap: true, // or 'inline' | 'hidden'

// Target browsers
target: 'esnext', // or 'es2015', 'es2020', etc.

// CSS code splitting
cssCodeSplit: true

} });

Advanced Chunking Strategy

export default defineConfig({ build: { rollupOptions: { output: { manualChunks(id) { // All node_modules in vendor chunk if (id.includes('node_modules')) { // Split large vendors if (id.includes('framer-motion')) { return 'vendor-animation'; } if (id.includes('react') || id.includes('react-dom')) { return 'vendor-react'; } return 'vendor'; }

      // Component-based splitting
      if (id.includes('/components/views/')) {
        const viewName = id.split('/components/views/')[1].split('.')[0];
        return `view-${viewName.toLowerCase()}`;
      }
    }
  }
}

} });

Compression and Minification

import { defineConfig } from 'vite'; import { compression } from 'vite-plugin-compression2';

// Install: pnpm add -D vite-plugin-compression2 export default defineConfig({ plugins: [ // Gzip compression compression({ algorithm: 'gzip', include: /.(js|css|html|svg)$/ }),

// Brotli compression
compression({
  algorithm: 'brotliCompress',
  include: /\.(js|css|html|svg)$/
})

],

build: { // esbuild is faster, terser produces smaller output minify: 'terser', terserOptions: { compress: { drop_console: true, // Remove console.log in production drop_debugger: true } } } });

CSS and Styling

PostCSS and Tailwind Integration

// vite.config.ts export default defineConfig({ css: { postcss: './postcss.config.js',

// CSS modules configuration
modules: {
  localsConvention: 'camelCase',
  scopeBehaviour: 'local'
},

// Preprocessor options
preprocessorOptions: {
  scss: {
    additionalData: `@import "@/styles/variables.scss";`
  }
}

} });

// postcss.config.js export default { plugins: { 'tailwindcss': {}, 'autoprefixer': {} } };

CSS Code Splitting

export default defineConfig({ build: { cssCodeSplit: true, // Split CSS per chunk

rollupOptions: {
  output: {
    assetFileNames: (assetInfo) => {
      // Organize CSS files
      if (assetInfo.name?.endsWith('.css')) {
        return 'css/[name]-[hash][extname]';
      }
      return 'assets/[name]-[hash][extname]';
    }
  }
}

} });

Static Assets

Asset Handling Patterns

// Importing assets (returns URL string) import logo from './assets/logo.png'; import styles from './styles.module.css';

// Explicit URL imports import assetUrl from './asset.png?url';

// Raw content import import rawSvg from './icon.svg?raw';

// Worker import import Worker from './worker?worker';

// JSON import import data from './data.json';

Public Directory

/public /images logo.svg /fonts custom-font.woff2 favicon.ico

// Public assets are served at root and NOT processed // Reference with absolute path <img src="/images/logo.svg" alt="Logo" />

// ❌ Don't import from public // import logo from '/public/images/logo.svg'; // Wrong!

// ✅ Import from src/assets for processing import logo from '@/assets/logo.svg'; // Correct

Asset Configuration

export default defineConfig({ // Public base path base: '/', // or '/my-app/' for subdirectory hosting

publicDir: 'public', // Default

build: { assetsDir: 'assets', // Output directory for assets assetsInlineLimit: 4096, // Inline assets < 4kb as base64

rollupOptions: {
  output: {
    assetFileNames: (assetInfo) => {
      const info = assetInfo.name.split('.');
      const ext = info[info.length - 1];

      // Organize by file type
      if (/png|jpe?g|svg|gif|webp|ico/i.test(ext)) {
        return 'images/[name]-[hash][extname]';
      }
      if (/woff2?|ttf|otf|eot/i.test(ext)) {
        return 'fonts/[name]-[hash][extname]';
      }
      return 'assets/[name]-[hash][extname]';
    }
  }
}

} });

Preview Mode

Preview Production Build

export default defineConfig({ preview: { port: 4173, strictPort: true, open: true,

// Proxy config (same as dev server)
proxy: {
  '/api': 'http://localhost:3000'
},

// CORS
cors: true,

// Headers
headers: {
  'Cache-Control': 'public, max-age=31536000'
}

} });

Commands:

Build for production

pnpm vite build

Preview production build locally

pnpm vite preview

Preview on specific port

pnpm vite preview --port 8080

Vite 7 Notes

Vite 7.x introduces:

  • Rolldown - New bundler written in Rust for faster builds (optional)

  • Improved TypeScript support

  • Better tree-shaking

  • Enhanced HMR performance

For demos, the default configuration works well. Advanced bundler options are not typically needed.

Complete Production Config

import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { compression } from 'vite-plugin-compression2'; import path from 'node:path';

export default defineConfig(({ mode }) => { const isDev = mode === 'development';

return { plugins: [ react(), // Compression for production (requires vite-plugin-compression2) !isDev && compression({ algorithm: 'gzip', include: /.(js|css|html|svg)$/ }), !isDev && compression({ algorithm: 'brotliCompress', include: /.(js|css|html|svg)$/ }) ].filter(Boolean),

resolve: {
  alias: {
    '@': path.resolve(__dirname, './src'),
    '@components': path.resolve(__dirname, './src/components'),
    '@hooks': path.resolve(__dirname, './src/hooks'),
    '@utils': path.resolve(__dirname, './src/utils'),
    '@types': path.resolve(__dirname, './src/types')
  }
},

server: {
  port: 5173,
  strictPort: true,
  open: true,
  hmr: {
    overlay: true
  },
  proxy: {
    '/api': {
      target: 'http://localhost:3000',
      changeOrigin: true
    }
  }
},

build: {
  outDir: 'dist',
  sourcemap: !isDev,
  minify: isDev ? false : 'terser',
  terserOptions: {
    compress: {
      drop_console: true,
      drop_debugger: true
    }
  },
  rollupOptions: {
    output: {
      manualChunks: {
        'react-vendor': ['react', 'react-dom'],
        'router-vendor': ['react-router-dom'],
        'animation-vendor': ['framer-motion']
      },
      assetFileNames: (assetInfo) => {
        const info = assetInfo.name.split('.');
        const ext = info[info.length - 1];
        if (/png|jpe?g|svg|gif|webp|ico/i.test(ext)) {
          return 'images/[name]-[hash][extname]';
        }
        if (/woff2?|ttf|otf|eot/i.test(ext)) {
          return 'fonts/[name]-[hash][extname]';
        }
        return 'assets/[name]-[hash][extname]';
      },
      chunkFileNames: 'js/[name]-[hash].js',
      entryFileNames: 'js/[name]-[hash].js'
    }
  },
  chunkSizeWarningLimit: 500
},

preview: {
  port: 4173,
  strictPort: true,
  open: true
}

}; });

Best Practices

  • Use path aliases for clean imports and avoid ../../../ hell

  • Prefix client env vars with VITE_ for automatic exposure

  • Split large vendors into separate chunks for better caching

  • Enable compression for production builds (gzip + brotli)

  • Use .env.local for secrets and never commit to git

  • Configure proxy for API calls to avoid CORS in development

  • Preview builds locally before deploying to catch issues

  • Organize assets by type in build output for better CDN caching

  • Enable sourcemaps in production for debugging (or use 'hidden')

  • Use esbuild for faster builds, terser for smaller output

  • Set base path correctly for subdirectory deployments

  • Test HMR after config changes to ensure Fast Refresh works

Anti-Patterns

  • ❌ Forgetting VITE_ prefix on environment variables

  • ❌ Importing from /public directory instead of src/assets

  • ❌ Committing .env.local with API keys

  • ❌ Not configuring path aliases (causes messy imports)

  • ❌ Using terser in development (unnecessary slowdown)

  • ❌ Disabling CSS code splitting for large apps

  • ❌ Not setting strictPort (silent port conflicts)

  • ❌ Ignoring chunk size warnings (impacts load time)

  • ❌ Missing tsconfig.json paths when using aliases

  • ❌ Hardcoding localhost URLs (use env vars)

  • ❌ Not testing preview mode before deployment

  • ❌ Placing all vendors in single chunk (defeats caching)

  • ❌ Configuring proxy for demos (demos are static, no backend)

Feedback Loops

Dev server performance:

Check HMR speed

Should be < 50ms for most updates

Chrome DevTools → Network → Filter by "vite"

Build analysis:

Analyze bundle size

pnpm vite build --mode production

Output shows chunk sizes

dist/js/vendor-react-abc123.js 142.34 kB

dist/js/index-def456.js 87.21 kB

Preview testing:

Always preview before deploying

pnpm vite build && pnpm vite preview

Test:

- All routes work

- Assets load correctly

- API proxy works (if configured)

- No console errors

Environment validation:

// Add runtime checks for required env vars if (!import.meta.env.VITE_API_URL) { throw new Error('VITE_API_URL is required'); }

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

agile-methodology

No summary provided by upstream source.

Repository SourceNeeds Review
General

execution-roadmaps

No summary provided by upstream source.

Repository SourceNeeds Review
General

designing-systems

No summary provided by upstream source.

Repository SourceNeeds Review
General

estimating-work

No summary provided by upstream source.

Repository SourceNeeds Review