Vite Configuration for a React App

Vite has become the dominant build tool for modern React, Vue, and Svelte applications because it solves the two most painful aspects of traditional bundler-based development: slow startup times and slow hot module replacement. Where webpack rebuilds and re-bundles the entire dependency graph on startup, Vite serves source files directly using native ES modules during development and only transforms changed modules when they're requested. A cold start that takes 30 seconds in webpack takes under a second in Vite. This configuration covers the four most commonly needed Vite customizations. The plugins: [react()] line is required for JSX transformation and React Fast Refresh (which preserves component state during hot reloads). Without this plugin, JSX syntax causes a parse error and hot reloads lose component state on every save. Path alias resolution with resolve.alias: the @ alias maps to the src directory, enabling clean imports like import Button from '@/components/Button' instead of '../../../components/Button'. This alias must be configured in two places: vite.config.ts (so Vite's bundler resolves the path correctly) and tsconfig.json's paths option (so TypeScript's type checker resolves the same paths). They must match exactly — a mismatch causes TypeScript to report import errors that Vite itself doesn't care about. The development proxy is one of Vite's most practical features: any request to the Vite dev server that starts with /api is forwarded to http://localhost:8000. The browser sees both the frontend and the API as being on the same origin (localhost:3000), so CORS headers are not needed during development. changeOrigin: true rewrites the Host header to match the target server. The rewrite function strips the /api prefix before forwarding. Build optimization with manualChunks: by default, Vite bundles everything into one JavaScript file. With manualChunks: { vendor: ['react', 'react-dom'] }, the react and react-dom libraries are split into a separate vendor.js chunk. Since React's version changes infrequently, this chunk gets a long cache lifetime in browsers. Only the main application chunk needs to be re-downloaded when your application code changes. Real-world customizations: adding import.meta.env support for environment variables, configuring separate chunks for different parts of a large application (reducing initial bundle size), enabling Progressive Web App support with the vite-plugin-pwa plugin, and integrating Storybook with the same Vite configuration. Tips: use vite build --analyze to generate a visual bundle analysis report showing which packages contribute most to your bundle size. This helps identify which packages are candidates for code-splitting or replacement with smaller alternatives.

Example
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'),
      '@components': path.resolve(__dirname, './src/components'),
    },
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/api/, ''),
      },
    },
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: { vendor: ['react', 'react-dom'] },
      },
    },
  },
});
[ open in JSON Formatter → ]

FAQ

How do I set up path aliases in Vite?
Configure the alias in resolve.alias in vite.config.ts and add matching paths in tsconfig.json. Both must match for TypeScript to resolve types and Vite to bundle correctly.
What does the Vite dev proxy do?
The proxy forwards requests from the Vite dev server to a separate backend server. This avoids CORS issues during development by making the browser think both frontend and API are on the same origin.
Why split vendor chunks in the build?
Separating vendor libraries (React, Lodash, etc.) into a separate chunk means that when only your application code changes, users' browsers can use the cached vendor bundle without re-downloading it.

Related Examples