Vite’s magic is that it doesn’t bundle your code during development; it serves it directly to the browser over native ES modules.

Let’s see this in action. Imagine you have a simple index.html and main.js:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite Dev Server</title>
</head>
<body>
    <div id="app"></div>
    <script type="module" src="/main.js"></script>
</body>
</html>
// main.js
import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

When you run npm run dev (assuming Vite is set up), Vite starts a development server. Your browser requests /main.js. Vite intercepts this, sees the import { createApp } from 'vue', and instead of bundling, it tells the browser: "Okay, you want vue? Here’s the Vue library directly." It might serve a file like /node_modules/.vite/deps/vue.js?v=abcdef12. The browser then requests that file. This continues for every import, creating a dependency graph on the fly.

The vite.config.ts file is your control panel for this entire process. It’s a standard TypeScript (or JavaScript) file that exports a configuration object.

Here’s a breakdown of the most common configuration options:

root

This option specifies the project root directory. By default, it’s the directory containing vite.config.ts. If your index.html and package.json are in a subdirectory, you’d set it there.

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

export default defineConfig({
  root: './src', // Your index.html and package.json are in ./src
});

base

This is the public base path your application will be served at. It’s crucial for production builds, especially if you’re deploying to a subdirectory. It defaults to /.

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

export default defineConfig({
  base: '/my-app/', // Your app will be served from https://yourdomain.com/my-app/
});

publicDir

This directory is served at the root of your base path. Files in publicDir are copied directly to the build output without processing. It defaults to public.

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

export default defineConfig({
  publicDir: 'static', // Files in ./static will be served from /static/
});

build

This object contains configuration for the production build.

outDir

The directory to output the optimized build. Defaults to dist.

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

export default defineConfig({
  build: {
    outDir: 'build', // Production assets will be in ./build
  },
});

assetsDir

Directory to place assets within outDir. Defaults to assets.

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

export default defineConfig({
  build: {
    assetsDir: 'static-assets', // Assets will be in build/static-assets/
  },
});

sourcemap

Generate source maps for production build. Can be true, false, or 'inline'.

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

export default defineConfig({
  build: {
    sourcemap: true, // Generates .js.map files
  },
});

minify

Enable or disable minification, or choose a specific minifier. Defaults to esbuild.

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

export default defineConfig({
  build: {
    minify: 'terser', // Use Terser for minification (more powerful but slower than esbuild)
  },
});

server

Configuration for the development server.

port

The port to run the dev server on. Defaults to 5173.

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

export default defineConfig({
  server: {
    port: 3000, // Dev server will run on http://localhost:3000
  },
});

proxy

Configure network proxy. This is incredibly useful for forwarding API requests to a backend server during development.

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

export default defineConfig({
  server: {
    proxy: {
      // string shorthand: '/api' -> 'http://localhost:3000/api'
      '/api': 'http://localhost:3000',
      // with options
      '/foo': {
        target: 'http://127.0.0.1:7001',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/foo/, ''),
      },
    },
  },
});

Here, requests to /api (e.g., http://localhost:5173/api/users) will be forwarded to http://localhost:3000/api/users. changeOrigin: true is important for virtual hosted sites. rewrite lets you change the path before it’s sent to the target.

plugins

An array of Vite plugins. Plugins extend Vite’s functionality, handling different file types or adding custom transformations.

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; // Example for Vue 3

export default defineConfig({
  plugins: [vue()], // Register the Vue plugin
});

resolve

Configuration for module resolution.

alias

Set up module aliases for easier imports.

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

export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'), // Alias '@' to './src'
      'utils': path.resolve(__dirname, './src/utils'), // Alias 'utils' to './src/utils'
    },
  },
});

With this alias, you can import from '@/components/MyComponent.vue' instead of from '../../src/components/MyComponent.vue'.

css

CSS configuration options.

preprocessorOptions

Inject options for CSS preprocessors.

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

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "./src/styles/variables.scss";', // Make variables available in all .scss files
      },
    },
  },
});

The most surprising thing about Vite’s configuration is how it leverages a plugin system that’s deeply integrated with Rollup, but with significant optimizations for the development server. This allows for a unified configuration that handles both lightning-fast dev builds and highly optimized production bundles.

The transformIndexHtml hook within plugins is a powerful, often overlooked feature. It allows you to intercept and modify the index.html file itself before it’s served or included in the build. You can inject scripts, modify meta tags, or even conditionally render content based on the environment.

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

export default defineConfig({
  plugins: [
    {
      name: 'html-transform',
      transformIndexHtml(html) {
        // Example: Inject a script tag
        return html.replace(
          '<body>',
          '<body><script>console.log("Vite is awesome!");</script>'
        );
      },
    },
  ],
});

This hook is executed for both development and production builds, offering a consistent way to manipulate your HTML entry point.

Understanding vite.config.ts is key to unlocking Vite’s full potential, from customizing the dev experience to fine-tuning production output.

The next step is often delving into specific plugin configurations or exploring advanced build optimizations.

Want structured learning?

Take the full Vite course →