Vite doesn’t just use Rollup; it is Rollup under the hood for production builds, and it exposes most of Rollup’s configuration directly.

Let’s see Vite pass some options to Rollup to tweak how our app gets bundled. Imagine we have a simple Vite project, and we want to customize Rollup’s output.

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

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        // Example: Customize chunk naming
        chunkFileNames: 'assets/chunks/[name]-[hash].js',
        entryFileNames: 'assets/entries/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',

        // Example: Manual chunks for better caching
        manualChunks(id) {
          if (id.includes('node_modules')) {
            // Vendor code goes into a 'vendor' chunk
            return 'vendor';
          }
        },
      },
      // Example: Add a Rollup plugin
      plugins: [
        // This is a hypothetical plugin for demonstration.
        // In a real scenario, you'd import actual Rollup plugins.
        {
          name: 'my-custom-rollup-plugin',
          transform(code, id) {
            if (id.endsWith('.js')) {
              return code.replace('process.env.NODE_ENV', '"production"');
            }
          },
        },
      ],
      // Example: Externalize dependencies
      external: ['react', 'react-dom'],
    },
  },
});

When you run vite build, Vite takes these rollupOptions and passes them directly into its internal Rollup configuration. Rollup then uses these settings to bundle your application.

The build.rollupOptions object in vite.config.js is your gateway to controlling Rollup’s core behavior during the production build.

Here’s what’s happening under the hood:

  • output: This sub-object configures how Rollup generates the final output files.
    • chunkFileNames, entryFileNames, assetFileNames: These use Rollup’s templating to define the structure and naming of your bundled JavaScript chunks, entry points, and static assets. The [name] and [hash] placeholders are crucial for cache busting and organizing your output. For instance, assets/entries/[name]-[hash].js will place your main entry files into an assets/entries directory, named after their original module and a unique hash.
    • manualChunks: This is a powerful function that allows you to dictate how Rollup splits your code into separate chunks. By returning a string (the chunk name), you tell Rollup to group modules matching that condition into that specific chunk. The example if (id.includes('node_modules')) { return 'vendor'; } is a classic strategy to bundle all your third-party dependencies into a single vendor.js file, which can be cached independently of your application code.
  • plugins: This array is where you can inject any Rollup plugins you need. Vite has its own set of plugins that it uses internally, but you can add your own custom ones or community plugins here to perform tasks like code transformation, optimization, or even generating additional files. The example plugin demonstrates a simple transformation that replaces process.env.NODE_ENV with "production", a common task for optimizing code for production.
  • external: This option tells Rollup which modules should not be bundled into your output. Instead, Rollup will assume these modules are available in the environment where your application will run (e.g., via a CDN or globally installed). This is particularly useful for large libraries that you want to be loaded separately, potentially improving initial load times if they are already cached by the browser.

This configuration allows you to fine-tune the bundling process for performance, caching, and organization. For example, by strategically using manualChunks, you can ensure that frequently updated application code is separated from infrequently changing vendor code, leading to better browser caching.

The most surprising thing is that Vite doesn’t replace Rollup’s core bundling logic; it orchestrates it. Vite’s magic lies in its development server (using esbuild) and its intelligent default configuration for Rollup, but the production build is fundamentally Rollup.

You can often find specific Rollup plugin options by looking at the documentation for the plugin you want to use, as Vite simply passes them through.

The next thing you’ll likely want to explore is how Vite’s own build options, like build.minify and build.sourcemap, interact with and can be overridden by Rollup’s options.

Want structured learning?

Take the full Vite course →