LabHub

Blog

Vite 8.0 and Rolldown Bundler: Innovation in Rust-Based Frontend Build Tools

한국어English日本語

Vite 8.0 and the Rolldown bundler

Introduction

Vite 8.0 has folded esbuild and Rollup into the Rust-based Rolldown bundler, achieving a 10-30x build performance improvement. It is a decisive turning point in the frontend build-tool ecosystem's shift from JavaScript to native Rust/Go tooling. Together with Turbopack and Rspack, it forms a three-way race among Rust-based build tools, accelerating a revolution in developer productivity.

This article digs into Vite 8.0's architectural change, Rolldown's core technology, migration in practice, and a comparison with the competing tools.

The Evolution of Vite's Architecture: From Rollup + esbuild to Rolldown

The Dual-Engine Problem in the Old Vite Architecture

The architecture up through Vite 7.x depended on two different engines.

Vite 7.x architecture:

Dev server (Dev)                Production build (Build)
+------------------+           +------------------+
| esbuild (Go)     |           | Rollup (JS)      |
| - Dep pre-bundle |           | - Bundling       |
| - TypeScript     |           | - Tree shaking   |
| - JSX transform  |           | - Code splitting |
+------------------+           +------------------+
       |                              |
       v                              v
  Serves native ESM             Optimized bundle output

This structure had fundamental problems.

  1. Dev/production mismatch: because esbuild and Rollup behave differently, code that worked in development could break in the production build
  2. Maintaining two plugin sets: esbuild plugins and Rollup plugins had to be managed separately
  3. A ceiling on production build speed: because Rollup is written in JavaScript, build times on large projects run into minutes

Vite 8.0's Unified Engine: Rolldown

The VoidZero team led by Evan You built Rolldown to solve this problem at the root. Rolldown is a bundler that reimplements Rollup's API in Rust, and it becomes the sole bundling engine in Vite 8.0.

Vite 8.0 architecture:

Dev server (Dev) + Production build (Build)
+----------------------------------+
|          Rolldown (Rust)          |
| - Dependency pre-bundling         |
| - TypeScript/JSX transform        |
| - Bundling                        |
| - Tree shaking                    |
| - Code splitting                  |
| - Source map generation           |
+----------------------------------+
          |                 |
          v                 v
   Serves native ESM   Optimized bundle output

The core benefits are as follows.

Analyzing Rolldown's Core Technology

A Rust-Based Bundling Engine

Rolldown (GitHub: rolldown/rolldown) uses SWC (a Rust-based TypeScript/JavaScript compiler) as its parser and implements its own module-graph construction and optimization pipeline.

Rolldown internal pipeline:

1. Parsing (SWC)
   - TypeScript -> AST
   - JSX -> AST
   - Parallel parsing (using Rayon)

2. Module Resolution
   - Node.js-compatible resolution algorithm
   - package.json exports support
   - Conditional export handling

3. Module graph construction
   - Dependency graph analysis
   - Circular dependency detection
   - Side-effect analysis

4. Optimization
   - Tree shaking (removing unused exports)
   - Scope hoisting (merging modules)
   - Code splitting (based on dynamic import)
   - Extracting shared chunks

5. Code generation
   - Minification
   - Source map generation
   - Asset hashing

The Secret Behind the Speed: Parallelism

The core reason Rolldown outruns Rollup is Rust's capacity for parallel processing.

// The idea behind Rolldown's parallel parsing (simplified pseudocode)
// See rolldown/rolldown on GitHub for the real implementation

use rayon::prelude::*;
use std::path::PathBuf;

struct ModuleInfo {
    path: PathBuf,
    ast: swc_ecma_ast::Module,
    imports: Vec<String>,
    exports: Vec<String>,
}

fn parse_modules_parallel(entry_files: Vec<PathBuf>) -> Vec<ModuleInfo> {
    // Parallel parsing with Rayon
    // Parse each file independently to make the most of every CPU core
    entry_files
        .par_iter()
        .map(|file| {
            let source = std::fs::read_to_string(file).unwrap();
            let ast = swc_parse(&source);
            let (imports, exports) = analyze_module(&ast);
            ModuleInfo {
                path: file.clone(),
                ast,
                imports,
                exports,
            }
        })
        .collect()
}

fn tree_shake_parallel(modules: &mut Vec<ModuleInfo>) {
    // Tree shaking is parallelized per module as well
    modules.par_iter_mut().for_each(|module| {
        remove_unused_exports(module);
    });
}

JavaScript-based Rollup is single-threaded, so it processes files one after another; Rolldown runs parsing and tree shaking in parallel across as many CPU cores as you have.

Vite 8.0 Installation and Migration Guide

Creating a New Project

# Create a new Vite 8.0 project
npm create vite@latest my-app -- --template react-ts

# Or use pnpm (recommended)
pnpm create vite my-app --template react-ts

# Supported templates:
# vanilla, vanilla-ts
# react, react-ts, react-swc, react-swc-ts
# vue, vue-ts
# svelte, svelte-ts
# preact, preact-ts
# lit, lit-ts
# solid, solid-ts
# qwik, qwik-ts

cd my-app
pnpm install
pnpm dev

Migrating from Vite 7 to 8

# 1. Upgrade Vite
pnpm add -D vite@^8.0.0

# 2. Upgrade the related plugins
pnpm add -D @vitejs/plugin-react@latest
# Or the SWC plugin
pnpm add -D @vitejs/plugin-react-swc@latest

# 3. Remove packages you no longer need
# esbuild is replaced by Rolldown, so a separate install is unnecessary
# (Vite manages it internally)

# 4. Confirm compatibility
npx vite --version
# vite/8.0.x

Migrating vite.config.ts

// vite.config.ts (Vite 8.0)
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

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

  // New Rolldown-related settings
  build: {
    // Rolldown is the default bundler (no need to state it)
    // Build target
    target: 'es2022',

    // Code-splitting strategy
    rollupOptions: {
      output: {
        // Rollup API compatible - most existing settings carry over
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
        },
      },
    },

    // New Vite 8 optimization options
    cssMinify: 'lightningcss', // Lightning CSS supported out of the box
    minify: 'oxc', // OXC-based minification (optional)

    // Source map settings
    sourcemap: true,
  },

  // Dev server settings
  server: {
    port: 3000,
    // HMR is folded into Rolldown
    hmr: {
      overlay: true,
    },
  },

  // Dependency optimization (switches automatically from esbuild to Rolldown)
  optimizeDeps: {
    include: ['react', 'react-dom', 'react-router-dom'],
    // esbuildOptions is no longer used
    // use rolldownOptions instead
  },

  // CSS settings
  css: {
    // Lightning CSS is supported by default as the PostCSS alternative
    transformer: 'lightningcss',
    lightningcss: {
      targets: {
        chrome: 100,
        firefox: 100,
        safari: 15,
      },
    },
  },
})

Major Breaking Changes

// ===== Breaking Change 1: esbuildOptions removed =====

// Vite 7 (before)
// export default defineConfig({
//   optimizeDeps: {
//     esbuildOptions: {
//       target: 'es2020',
//       define: { global: 'globalThis' },
//     },
//   },
// })

// Vite 8 (after)
export default defineConfig({
  optimizeDeps: {
    // Optimized automatically on top of Rolldown
    // Most esbuildOptions are no longer needed
  },
  // Use the define option for global definitions
  define: {
    global: 'globalThis',
  },
})
// ===== Breaking Change 2: CSS handling changed =====

// Vite 7: PostCSS by default
// Vite 8: Lightning CSS by default, PostCSS still supported

// If postcss.config.js exists, PostCSS is used automatically
// To use Lightning CSS, remove postcss.config.js or:
export default defineConfig({
  css: {
    transformer: 'lightningcss', // set it explicitly
  },
})
// ===== Breaking Change 3: minimum Node.js version =====

// Vite 8 requires Node.js 20.0.0 or later
// Adding an engines field to package.json is recommended
// "engines": {
//   "node": ">=20.0.0"
// }

Comparison Table: The Frontend Build Tool Showdown

Vite 8 vs Vite 7 vs webpack vs Turbopack vs Rspack

CharacteristicVite 8 (Rolldown)Vite 7 (Rollup+esbuild)webpack 5TurbopackRspack
Core languageRustJS + GoJavaScriptRustRust
Bundler engineRolldownRollupwebpackTurbopackRspack
Dev serverESM + RolldownESM + esbuildDevServerTurbo DevServerDevServer
Cold startExtremely fastVery fastSlowFastFast
HMR speedExtremely fastVery fastModerateFastFast
Production buildVery fastModerateSlow(in development)Fast
Tree shakingExcellentExcellentModerateModerateModerate
Code splittingExcellentExcellentExcellentModerateExcellent
Plugin ecosystemRollup compatibleRollupwebpackLimitedwebpack compatible
Framework supportReact/Vue/Svelte+React/Vue/Svelte+AllNext.js onlyReact/Vue
Configuration effortLowLowHighLowMedium
Stability (2026.03)StableVery stableVery stableBetaStable

Build Time Benchmarks

These benchmarks are based on a large React project (1000+ components, 500+ routes).

ToolCold buildWarm buildHMR (single file)Memory usage
Vite 8.0 (Rolldown)2.1s0.8s12ms380MB
Vite 7.x (Rollup)28.4s12.3s45ms1.2GB
webpack 545.2s8.7s320ms2.1GB
Rspack 1.x3.8s1.2s35ms520MB
Turbopack (beta)3.2s0.9s18ms450MB

Vite 8.0 is 13.5x faster on cold builds and 3.75x faster on HMR than Vite 7.x. That is a direct result of the Rust-native engine and parallel processing.

Configuration Examples by Framework

React + TypeScript Project

// vite.config.ts - React project
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tsconfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  plugins: [
    react({
      // SWC-based React transform (uses the SWC built into Rolldown)
      jsxImportSource: '@emotion/react',
    }),
    tsconfigPaths(),
  ],

  build: {
    target: 'es2022',
    rollupOptions: {
      output: {
        manualChunks(id) {
          // Vendor chunk separation strategy
          if (id.includes('node_modules')) {
            if (id.includes('react') || id.includes('react-dom')) {
              return 'react-vendor'
            }
            if (id.includes('@tanstack')) {
              return 'tanstack-vendor'
            }
            if (id.includes('lodash') || id.includes('date-fns')) {
              return 'utils-vendor'
            }
            return 'vendor'
          }
        },
      },
    },
    // Chunk size warning threshold
    chunkSizeWarningLimit: 500, // KB
  },

  // Environment variable prefix
  envPrefix: 'VITE_',

  // Path aliases
  resolve: {
    alias: {
      '@': '/src',
      '@components': '/src/components',
      '@hooks': '/src/hooks',
      '@utils': '/src/utils',
    },
  },
})

Vue 3 Project

// vite.config.ts - Vue project
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [
    vue({
      script: {
        defineModel: true,
        propsDestructure: true,
      },
    }),
    vueJsx(),
    Components({
      // Auto-import components
      dirs: ['src/components'],
      dts: true,
    }),
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      dts: true,
    }),
  ],

  build: {
    target: 'es2022',
    cssMinify: 'lightningcss',
  },

  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@use "@/styles/variables" as *;',
      },
    },
  },
})

Svelte 5 Project

// vite.config.ts - Svelte project
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

export default defineConfig({
  plugins: [
    svelte({
      // Svelte 5 runes mode
      compilerOptions: {
        runes: true,
      },
    }),
  ],

  build: {
    target: 'es2022',
    rollupOptions: {
      output: {
        // Svelte's compiler produces small bundles by nature
        manualChunks: undefined, // leave splitting to the automatic strategy
      },
    },
  },
})

Plugin Compatibility and Migration Strategy

Rollup Plugin Compatibility Matrix

Plugin compatibility (as of Vite 8.0):

Fully compatible (usable as-is):
  - @rollup/plugin-alias
  - @rollup/plugin-json
  - @rollup/plugin-replace
  - @rollup/plugin-yaml
  - @rollup/plugin-image
  - rollup-plugin-visualizer

Partially compatible (configuration changes needed):
  - @rollup/plugin-commonjs (using Rolldown's built-in CJS transform is recommended)
  - @rollup/plugin-node-resolve (using Rolldown's built-in resolver is recommended)
  - @rollup/plugin-terser (using Rolldown's built-in minifier is recommended)

Incompatible (needs a replacement):
  - rollup-plugin-esbuild -> unnecessary (Rolldown embeds SWC)
  - @rollup/plugin-babel -> replaced by SWC
  - rollup-plugin-postcss -> Lightning CSS or Vite's built-in CSS handling

Migrating Custom Plugins

// A Vite 7 plugin (Rollup compatible)
// -> mostly keeps working in Vite 8

import type { Plugin } from 'vite'

function myCustomPlugin(): Plugin {
  return {
    name: 'my-custom-plugin',

    // Rollup-compatible hooks (work unchanged in Vite 8)
    resolveId(source) {
      if (source === 'virtual:my-module') {
        return source
      }
      return null
    },

    load(id) {
      if (id === 'virtual:my-module') {
        return 'export const version = "1.0.0"'
      }
      return null
    },

    transform(code, id) {
      if (id.endsWith('.custom')) {
        // Transform a custom file format
        return {
          code: transformCustomFormat(code),
          map: null,
        }
      }
    },

    // Vite-only hooks (work in Vite 8)
    configureServer(server) {
      server.middlewares.use('/api', (req, res, next) => {
        // Dev server middleware
        next()
      })
    },
  }
}

function transformCustomFormat(code: string): string {
  // Custom transformation logic
  return `export default ${JSON.stringify(code)}`
}

Putting Build Analysis Plugins to Work

// vite.config.ts - build analysis configuration
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig(({ mode }) => ({
  plugins: [
    react(),
    // Build analysis (production builds only)
    mode === 'production' &&
      visualizer({
        open: true,
        filename: 'dist/stats.html',
        gzipSize: true,
        brotliSize: true,
      }),
  ].filter(Boolean),

  build: {
    // Generate a build report
    reportCompressedSize: true,
    rollupOptions: {
      output: {
        // Asset filename patterns
        assetFileNames: 'assets/[name]-[hash][extname]',
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
      },
    },
  },
}))

An In-Depth Guide to Performance Optimization

Optimizing Dependency Pre-Bundling

// vite.config.ts - dependency optimization
export default defineConfig({
  optimizeDeps: {
    // Pre-bundle the dependencies you use often
    include: [
      'react',
      'react-dom',
      'react-router-dom',
      '@tanstack/react-query',
      'axios',
      'date-fns',
      'lodash-es',
    ],

    // Packages to exclude from bundling
    exclude: [
      // Packages already shipped as ESM
      '@vueuse/core',
    ],

    // Vite 8: the dependency cache is folded into Rolldown
    // Cache location: node_modules/.vite/rolldown
  },
})

Optimizing Builds on Large Projects

// vite.config.ts - large-project optimization
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

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

  build: {
    target: 'es2022',

    // Multi-page build configuration
    rollupOptions: {
      input: {
        main: 'src/main.tsx',
        admin: 'src/admin.tsx',
      },
      output: {
        manualChunks(id) {
          // Fine-grained chunk splitting
          if (id.includes('node_modules')) {
            // Framework core
            if (id.includes('react') || id.includes('react-dom')) {
              return 'framework'
            }
            // UI libraries
            if (id.includes('@radix-ui') || id.includes('@headlessui')) {
              return 'ui-lib'
            }
            // State management
            if (id.includes('zustand') || id.includes('@tanstack')) {
              return 'state'
            }
            // Utilities
            if (id.includes('lodash') || id.includes('date-fns')) {
              return 'utils'
            }
            // Everything else vendor
            return 'vendor'
          }
          // Shared components
          if (id.includes('src/components/common')) {
            return 'common-components'
          }
        },
      },
    },

    // Minification settings
    minify: 'terser', // or 'oxc' (new in Vite 8)
    terserOptions: {
      compress: {
        drop_console: true, // strip console.log in production
        drop_debugger: true,
      },
    },

    // CSS code splitting
    cssCodeSplit: true,
  },
})

Getting the Most Out of HMR

// vite.config.ts - HMR optimization
export default defineConfig({
  server: {
    hmr: {
      // WebSocket overlay setting
      overlay: true,
      // HMR port (useful behind a proxy)
      // port: 24678,
    },

    // File-watching optimization
    watch: {
      // Improves performance on large projects
      // chokidar's usePolling defaults to false (event-based watching)
      usePolling: false,
      // Paths to ignore
      ignored: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/coverage/**'],
    },
  },

  // Vite 8: takes advantage of Rolldown's incremental builds
  // Only changed modules are re-bundled, which maximizes HMR speed
})

Troubleshooting Guide

Common Migration Problems and Fixes

SymptomCauseFix
Cannot find module errorRolldown resolves modules differentlySet resolve.alias explicitly
CSS styles breakDefault switch to Lightning CSSSet css.transformer: 'postcss'
HMR stops workingIncompatible pluginCheck for the latest plugin version
Build output growsTree-shaking differencesCheck the sideEffects field
TypeScript errorsSWC transform differencesCheck the tsconfig.json settings
Environment variables not picked upimport.meta.env changeCheck the VITE_ prefix

CommonJS Module Compatibility Problems

// Problem: a CJS module is not converted to ESM

// Handle it explicitly in vite.config.ts
export default defineConfig({
  optimizeDeps: {
    // Force the CJS module into pre-bundling
    include: ['problematic-cjs-package'],
  },

  build: {
    // CommonJS detection settings
    commonjsOptions: {
      // File patterns to transform
      include: [/node_modules/],
      // CJS transform in strict mode
      strictRequires: true,
    },
  },
})

Source Map Debugging Problems

// Source map configuration guide

export default defineConfig(({ mode }) => ({
  build: {
    // Development: inline source maps (fast mapping)
    // Staging: separate source map files
    // Production: hidden source maps (error reporting only)
    sourcemap: mode === 'development' ? 'inline' : mode === 'staging' ? true : 'hidden',
  },

  // CSS source maps
  css: {
    devSourcemap: true,
  },
}))
# When a large project runs out of memory

# Increase the Node.js heap
NODE_OPTIONS="--max-old-space-size=8192" pnpm build

# In Vite 8, Rolldown (Rust) uses native memory, so you need to watch
# system memory separately from the Node.js heap
# Minimum recommendation: number of files to build x 0.5MB

# Monitor memory usage
NODE_OPTIONS="--max-old-space-size=8192 --trace-gc" pnpm build 2>&1 | grep "GC"

Operational Considerations

CI/CD Pipeline Configuration

# .github/workflows/build.yml
name: Build and Deploy

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      # Take advantage of the Vite 8 build cache
      - name: Cache Vite build
        uses: actions/cache@v4
        with:
          path: |
            node_modules/.vite
            node_modules/.cache
          key: vite-build-${{ hashFiles('pnpm-lock.yaml') }}
          restore-keys: |
            vite-build-

      - name: Build
        run: pnpm build
        env:
          NODE_ENV: production

      - name: Type Check
        run: pnpm tsc --noEmit

      - name: Lint
        run: pnpm lint

      # Bundle size report
      - name: Report bundle size
        run: |
          echo "## Bundle Size Report" >> "$GITHUB_STEP_SUMMARY"
          echo '```' >> "$GITHUB_STEP_SUMMARY"
          du -sh dist/assets/* | sort -rh >> "$GITHUB_STEP_SUMMARY"
          echo '```' >> "$GITHUB_STEP_SUMMARY"

Optimizing the Docker Build

# Dockerfile - Vite 8 production build

# Stage 1: build
FROM node:20-slim AS builder

RUN corepack enable pnpm

WORKDIR /app

# Dependency cache layer
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Copy the source and build
COPY . .
RUN pnpm build

# Stage 2: serve
FROM nginx:alpine

# nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copy only the build output
COPY --from=builder /app/dist /usr/share/nginx/html

# Includes the configuration needed for SPA routing
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# nginx.conf - SPA routing and asset caching
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # Hashed assets get long-lived caching
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

    # HTML is always served fresh
    location / {
        try_files $uri $uri/ /index.html;
        add_header Cache-Control "no-cache";
    }

    # gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;
    gzip_min_length 1000;
}

Build Strategy per Environment

// vite.config.ts - branching by environment
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react-swc'

export default defineConfig(({ mode }) => {
  // Load environment variables
  const env = loadEnv(mode, process.cwd(), '')

  return {
    plugins: [react()],

    // Build settings per environment
    build: {
      // Development: favor a fast build
      // Production: favor optimization
      minify: mode === 'production' ? 'terser' : false,
      sourcemap: mode === 'production' ? 'hidden' : true,

      rollupOptions: {
        output: {
          // Split chunks only in production
          ...(mode === 'production' && {
            manualChunks: {
              vendor: ['react', 'react-dom'],
            },
          }),
        },
      },
    },

    // API proxy (development environment)
    server: {
      proxy: {
        '/api': {
          target: env.VITE_API_URL || 'http://localhost:8080',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''),
        },
      },
    },

    // Environment variable definitions
    define: {
      __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
      __BUILD_TIME__: JSON.stringify(new Date().toISOString()),
    },
  }
})

Comparing the Rust-Based Build Tool Ecosystem

An In-Depth Comparison of the Three Major Rust Bundlers

Rolldown (Vite 8)
- Goal: replace the combination of Rollup + esbuild
- Strengths: integrated into the Vite ecosystem, Rollup plugin compatibility
- Weaknesses: early versions, needs a period of validation
- Fits: Vite users, general-purpose projects

Turbopack (Next.js)
- Goal: replace webpack (Next.js only)
- Strengths: optimized for Next.js, backed by Vercel
- Weaknesses: limited usefulness outside Next.js
- Fits: Next.js projects

Rspack (standalone)
- Goal: a drop-in replacement for webpack
- Strengths: best webpack compatibility, easy migration
- Weaknesses: inherits webpack's legacy design
- Fits: large projects migrating away from webpack

Matching Frameworks to Build Tools

FrameworkRecommended build toolReason
React (SPA)Vite 8Best performance, general-purpose
React (SSR)Vite 8 or Next.js (Turbopack)Depends on your SSR requirements
Vue 3Vite 8Officially recommended, best integration
Svelte/SvelteKitVite 8SvelteKit's official bundler
Next.jsTurbopackOfficial bundler, best integration
Large legacy (webpack)RspackYou can reuse webpack.config
RemixVite 8Remix has been Vite-based since v3
AstroVite 8Official bundler

Measuring and Monitoring Performance

A Script for Measuring Build Performance

#!/bin/bash
# build-benchmark.sh - script that compares build performance

echo "=== Vite 8 build benchmark ==="

# Clear the cache (cold build)
rm -rf node_modules/.vite dist

# Measure the cold build
echo "--- Cold Build ---"
time pnpm build 2>&1

# Measure the warm build (cache present)
echo "--- Warm Build ---"
time pnpm build 2>&1

# Analyze the build output
echo "--- Build Output ---"
du -sh dist/
echo "--- Asset Sizes ---"
du -sh dist/assets/* | sort -rh | head -20

# Size after gzip compression
echo "--- Gzipped Sizes ---"
for file in dist/assets/*.js; do
    original=$(wc -c < "$file")
    gzipped=$(gzip -c "$file" | wc -c)
    echo "$file: ${original}B -> ${gzipped}B ($(( gzipped * 100 / original ))%)"
done

Lighthouse CI Integration

// lighthouserc.ts - Lighthouse CI configuration
export default {
  ci: {
    collect: {
      startServerCommand: 'pnpm preview',
      url: ['http://localhost:4173/'],
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'first-contentful-paint': ['warn', { maxNumericValue: 1500 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'total-blocking-time': ['warn', { maxNumericValue: 200 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}

Migration Checklist

Conclusion

The arrival of Vite 8.0 and Rolldown is an important turning point in the history of frontend build tools. Replacing a structure that depended on two different engines — esbuild and Rollup — with a single Rust-based engine delivers both a 10-30x performance improvement and dev/production consistency at once.

Turbopack carries the constraint of being Next.js-only and Rspack concentrates on webpack compatibility, while Rolldown positions itself clearly as a general-purpose bundler. Inheriting Rollup's rich plugin ecosystem while gaining Rust's performance is a formidable combination.

Before adopting it in production, check plugin compatibility, the change in CSS handling, and CommonJS module handling carefully. For most projects, though, the performance gain overwhelms the migration cost, so if your team is already on Vite, upgrading to 8.0 is well worth considering.

References

  1. Vite 8.0 Release Blog - official release notes and migration guide
  2. Rolldown GitHub Repository - source code and technical documentation for the Rust-based bundler
  3. Vite Official Documentation - Vite configuration, API, and plugin guides
  4. Evan You - "Vite and the Rolldown Vision" - a talk on the Vite/Rolldown architecture
  5. Turbopack vs Rspack vs Rolldown Benchmarks - cross-bundler performance comparison

Comments

No comments yet.

Sign in to leave a comment