How to Configure Your Vite Config File

How to Configure Your Vite Config File

Vite's configuration system is refreshingly straightforward compared to other build tools, but there's still plenty to know when you want to customize your setup.

Whether you're building a simple React app or a complex multi-framework project, understanding your vite.config.js file will save you hours of frustration.

In this article, I'll break down the basic structure of a vite.config and dive into the more nuanced properties you can configure.

The Basics: Your First Config File

When you create a new Vite project, you'll get a basic config file that looks something like this:

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

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

That defineConfig function isn't just for show – it provides TypeScript intellisense and helps catch configuration errors early. Always use it, even in JavaScript projects.

Essential Configuration Options

Development Server Settings

The server object controls how your development server behaves:

export default defineConfig({
  server: {
    port: 3000,
    host: true, // Listen on all addresses
    open: true, // Auto-open browser
    cors: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false
      }
    }
  }
})

The proxy configuration is particularly useful when your backend runs on a different port. No more CORS headaches during development.

Build Configuration

Control how Vite builds your production bundle:

export default defineConfig({
  build: {
    outDir: 'dist',
    sourcemap: true,
    minify: 'terser', // or 'esbuild'
    rollupOptions: {
      external: ['vue'], // Don't bundle Vue in a library
      output: {
        globals: {
          vue: 'Vue'
        }
      }
    }
  }
})

The build configuration gives you fine-grained control over your production output. The outDir specifies where the built files will be placed (defaulting to 'dist'), while sourcemap: true generates source maps for easier debugging in production.

The minify option lets you choose between 'terser' for maximum compression or 'esbuild' for faster builds with slightly larger output.

The rollupOptions section provides direct access to Rollup's configuration, which is particularly useful when building libraries. In this example, external: ['vue'] prevents Vue from being bundled into the library output, assuming it will be provided by the consuming application.

The corresponding globals mapping tells Rollup what global variable name to use when Vue is accessed externally. This pattern is essential for creating libraries that don't duplicate dependencies already present in the host application, reducing bundle size and avoiding version conflicts.

Path Resolution and Aliases

Set up clean import paths that make your code more maintainable:

import path from 'path'

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

Now you can write import Button from '@components/Button' instead of ../../components/Button.

Working with Plugins

Plugins are where Vite really shines. The ecosystem is rich and most plugins are zero-config:

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

export default defineConfig({
  plugins: [
    react(),
    // Add more plugins as needed
  ]
})

Vite's plugin architecture is built on top of Rollup's plugin system, extended with additional hooks for development-specific features like hot module replacement.

This design allows Vite to leverage the mature Rollup plugin ecosystem while adding its own optimizations. Most official plugins like @vitejs/plugin-react work out of the box with sensible defaults - simply installing and adding them to the plugins array is often all you need.

The ecosystem includes plugins for virtually every use case: TypeScript support, CSS preprocessing, PWA generation, bundle analysis, and framework-specific optimizations.

During development, plugins can hook into Vite's dev server to transform files on-the-fly, while in production they integrate seamlessly with the Rollup build process. This unified approach means you write your plugin configuration once and it works consistently across both development and production environments, eliminating the complex webpack loader configurations that plagued earlier build tools.

Popular plugins include @vitejs/plugin-vue for Vue projects, vite-plugin-pwa for Progressive Web Apps, and vite-plugin-eslint for linting integration.

Environment-Specific Configuration

You'll often need different settings for development and production:

export default defineConfig(({ command, mode }) => {
  const config = {
    plugins: [react()],
  }

  if (command === 'serve') {
    // Development-specific config
    config.server = {
      port: 3000,
    }
  } else {
    // Production build config
    config.build = {
      minify: 'terser',
    }
  }

  return config
})

This configuration function demonstrates Vite's ability to adapt based on the current environment. The function receives two parameters:

command (either 'serve' for development or 'build' for production) and mode (the current mode like 'development' or 'production'). By checking the command type, you can conditionally apply different settings.

In this example, when serving in development mode, it sets a custom port of 3000 for the development server. When building for production, it configures the build process to use Terser for JavaScript minification, which helps reduce bundle size.

CSS and Asset Handling

Vite handles CSS preprocessing and assets elegantly:

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`
      }
    },
    modules: {
      localsConvention: 'camelCase'
    }
  },
  assetsInclude: ['**/*.glb'] // Include additional asset types
})

This configuration showcases Vite's powerful CSS and asset processing capabilities.

The css.preprocessorOptions section allows you to configure CSS preprocessors like Sass/SCSS, where additionalData automatically injects imports into every SCSS file, making global variables and mixins available without manual imports.

The modules configuration controls CSS Modules behavior, with localsConvention: 'camelCase' converting kebab-case class names to camelCase when imported in JavaScript (so .my-class becomes styles.myClass).

The assetsInclude option extends Vite's built-in asset handling to recognize additional file types like .glb 3D model files, treating them as static assets that can be imported and referenced in your code. This unified approach to styling and assets eliminates the need for complex webpack loaders while providing intuitive defaults.

TypeScript Configuration

If you're using TypeScript, create a vite.config.ts file instead:

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

export default defineConfig({
  plugins: [react()],
  // TypeScript will provide full intellisense here
})

Make sure your tsconfig.json includes the config file and has proper module resolution settings.

Performance Optimization

A few tweaks can significantly improve build performance:

export default defineConfig({
  build: {
    target: 'esnext', // Use modern JS features
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash', 'date-fns']
        }
      }
    }
  },
  optimizeDeps: {
    include: ['react', 'react-dom'], // Pre-bundle these dependencies
    exclude: ['@your-company/internal-package']
  }
})

These optimizations target both build speed and runtime performance. Setting target: 'esnext' allows Vite to skip transpilation of modern JavaScript features, reducing build time and output size when targeting modern browsers.

The manualChunks configuration implements strategic code splitting by grouping related dependencies into separate chunks - this enables better browser caching since vendor libraries change less frequently than your application code. When users return to your site, they can reuse the cached vendor chunk even if your app code has updated.

The optimizeDeps section controls Vite's dependency pre-bundling during development. Including frequently-used dependencies like React ensures they're pre-processed and cached, speeding up initial dev server startup and subsequent hot reloads.

Conversely, excluding internal packages or dependencies that change frequently prevents unnecessary re-bundling. This configuration creates an optimal balance between development speed and production performance, with chunks sized appropriately for efficient network delivery and caching strategies.

Configuration File Organization

For complex projects, consider splitting your configuration:

// vite.config.js
import { defineConfig } from 'vite'
import { plugins } from './config/plugins'
import { server } from './config/server'
import { build } from './config/build'

export default defineConfig({
  plugins,
  server,
  build
})

This keeps your main config file clean and makes it easier to manage large configurations.

Testing Your Configuration

Always test your configuration changes in both development and production modes. Run npm run build and npm run preview to ensure everything works as expected. Consider setting up different configuration files for different environments if your needs are complex enough.

The beauty of Vite's configuration system is that it starts simple but scales with your project's complexity. Start with the basics and add configuration options as you need them. Most projects will do just fine with a minimal setup, and you can always extend it later when requirements change.

Walt is a computer scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.

Community Comments

No comments posted yet

Code Your Own Classic Snake Game – The Right Way

Master the fundamentals of game development and JavaScript with a step-by-step guide that skips the fluff and gets straight to the real code.

"Heavy scripts slowing down your site? I use Fathom Analytics because it’s lightweight, fast, and doesn’t invade my users privacy."
Ad Unit

Current Poll

Help us and the community figure out what the latest trends in coding are.

Total Votes:
Q:
Submit

Add a comment