A

Vite - Next Generation Frontend Tooling

vite build-tools bundler dev-server

Vite - Next Generation Frontend Tooling

Vite (French for "fast") is a modern build tool that provides an extremely fast development experience with instant server start and lightning-fast HMR.

Why Vite?

Instant Server Start

  • No bundling: Serves source files over native ESM
  • On-demand compilation: Only compiles files when requested
  • Cold start: < 1 second regardless of project size

Lightning Fast HMR

  • Instant updates: Changes reflect in < 100ms
  • Precise invalidation: Only affected modules reload
  • State preservation: Component state maintained

Optimized Production Builds

  • Rollup-based: Uses Rollup for production bundling
  • Tree-shaking: Removes unused code
  • Code splitting: Automatic chunking
  • Asset optimization: Minification, compression

Installation

# Create new Vite project
pnpm create vite my-app

# Or add to existing project
pnpm add -D vite

Configuration

Basic Config (vite.config.ts)

import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    port: 3000,
  },
  build: {
    outDir: 'dist',
  },
});

With React

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

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

With Vue

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

export default defineConfig({
  plugins: [vue()],
});

With Astro (Astro Vault)

import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  integrations: [react()],
  vite: {
    plugins: [tailwindcss()],
  },
});

Development Server

Start Dev Server

# Start on port 5173 (default)
pnpm vite

# Custom port
pnpm vite --port 3000

# Open browser automatically
pnpm vite --open

# Expose to network
pnpm vite --host

Hot Module Replacement

// HMR API (automatic for most frameworks)
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // Handle module update
  });
}

Production Build

Build for Production

# Build optimized bundles
pnpm vite build

# Build and analyze
pnpm vite build --mode production

# Preview production build
pnpm vite preview

Build Configuration

import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    target: 'esnext',
    outDir: 'dist',
    assetsDir: 'assets',
    minify: 'esbuild',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
        },
      },
    },
  },
});

Plugins

Official Plugins

pnpm add -D @vitejs/plugin-react    # React
pnpm add -D @vitejs/plugin-vue      # Vue 3
pnpm add -D @vitejs/plugin-legacy   # Legacy browsers
pnpm add -D vite-plugin-pwa         # PWA support
pnpm add -D vite-plugin-compression # Gzip/Brotli
pnpm add -D vite-imagetools         # Image optimization

Creating Custom Plugin

import type { Plugin } from 'vite';

export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    transform(code, id) {
      if (id.endsWith('.custom')) {
        return {
          code: transformCode(code),
          map: null,
        };
      }
    },
  };
}

Environment Variables

.env Files

# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App

Access in Code

// Access with import.meta.env
const apiUrl = import.meta.env.VITE_API_URL;
const title = import.meta.env.VITE_APP_TITLE;

// TypeScript support
interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
}

Asset Handling

Import Assets

// Import as URL
import logo from './logo.svg';

// Import as raw string
import shader from './shader.glsl?raw';

// Import as web worker
import Worker from './worker?worker';

Public Directory

public/
  favicon.ico
  robots.txt
  images/
    hero.jpg
<!-- Reference public assets with / -->
<img src="/images/hero.jpg" alt="Hero" />

CSS Processing

CSS Modules

/* styles.module.css */
.button {
  background: blue;
}
import styles from './styles.module.css';

<button className={styles.button}>Click</button>

PostCSS

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

CSS Preprocessors

pnpm add -D sass           # .scss, .sass
pnpm add -D less           # .less
pnpm add -D stylus         # .styl

Performance

Bundle Analysis

pnpm vite build --mode production

Build Stats

import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      open: true,
      gzipSize: true,
    }),
  ],
});

Optimization

Code Splitting

// Automatic code splitting
const Dashboard = lazy(() => import('./Dashboard'));

Preloading

// Preload module
const modulePromise = import('./heavy-module.js');

Asset Inlining

import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    assetsInlineLimit: 4096, // < 4kb inlined as base64
  },
});

Migration from Webpack

Key Differences

  • No webpack.config.js needed
  • Import paths use ES modules
  • Assets handled differently
  • Environment variables prefixed with VITE_

Example Migration

// webpack.config.js (before)
module.exports = {
  entry: './src/index.tsx',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
      },
    ],
  },
};

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

export default defineConfig({
  plugins: [react()], // TypeScript supported out of the box
});

Resources

Last updated: October 16, 2025