- Introduction
- The Evolution of Vite's Architecture: From Rollup + esbuild to Rolldown
- Analyzing Rolldown's Core Technology
- Vite 8.0 Installation and Migration Guide
- Comparison Table: The Frontend Build Tool Showdown
- Configuration Examples by Framework
- Plugin Compatibility and Migration Strategy
- An In-Depth Guide to Performance Optimization
- Troubleshooting Guide
- Operational Considerations
- Comparing the Rust-Based Build Tool Ecosystem
- Measuring and Monitoring Performance
- Migration Checklist
- Conclusion
- References

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.
- Dev/production mismatch: because esbuild and Rollup behave differently, code that worked in development could break in the production build
- Maintaining two plugin sets: esbuild plugins and Rollup plugins had to be managed separately
- 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.
- A single engine: development and production use the same engine, so the mismatch disappears
- Rust-native speed: parsing speed on the level of esbuild (Go) with optimization quality on the level of Rollup
- Rollup plugin compatibility: most existing Rollup plugins work unchanged
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
| Characteristic | Vite 8 (Rolldown) | Vite 7 (Rollup+esbuild) | webpack 5 | Turbopack | Rspack |
|---|---|---|---|---|---|
| Core language | Rust | JS + Go | JavaScript | Rust | Rust |
| Bundler engine | Rolldown | Rollup | webpack | Turbopack | Rspack |
| Dev server | ESM + Rolldown | ESM + esbuild | DevServer | Turbo DevServer | DevServer |
| Cold start | Extremely fast | Very fast | Slow | Fast | Fast |
| HMR speed | Extremely fast | Very fast | Moderate | Fast | Fast |
| Production build | Very fast | Moderate | Slow | (in development) | Fast |
| Tree shaking | Excellent | Excellent | Moderate | Moderate | Moderate |
| Code splitting | Excellent | Excellent | Excellent | Moderate | Excellent |
| Plugin ecosystem | Rollup compatible | Rollup | webpack | Limited | webpack compatible |
| Framework support | React/Vue/Svelte+ | React/Vue/Svelte+ | All | Next.js only | React/Vue |
| Configuration effort | Low | Low | High | Low | Medium |
| Stability (2026.03) | Stable | Very stable | Very stable | Beta | Stable |
Build Time Benchmarks
These benchmarks are based on a large React project (1000+ components, 500+ routes).
| Tool | Cold build | Warm build | HMR (single file) | Memory usage |
|---|---|---|---|---|
| Vite 8.0 (Rolldown) | 2.1s | 0.8s | 12ms | 380MB |
| Vite 7.x (Rollup) | 28.4s | 12.3s | 45ms | 1.2GB |
| webpack 5 | 45.2s | 8.7s | 320ms | 2.1GB |
| Rspack 1.x | 3.8s | 1.2s | 35ms | 520MB |
| Turbopack (beta) | 3.2s | 0.9s | 18ms | 450MB |
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
| Symptom | Cause | Fix |
|---|---|---|
Cannot find module error | Rolldown resolves modules differently | Set resolve.alias explicitly |
| CSS styles break | Default switch to Lightning CSS | Set css.transformer: 'postcss' |
| HMR stops working | Incompatible plugin | Check for the latest plugin version |
| Build output grows | Tree-shaking differences | Check the sideEffects field |
| TypeScript errors | SWC transform differences | Check the tsconfig.json settings |
| Environment variables not picked up | import.meta.env change | Check 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,
},
}))
Memory-Related Problems
# 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 /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
| Framework | Recommended build tool | Reason |
|---|---|---|
| React (SPA) | Vite 8 | Best performance, general-purpose |
| React (SSR) | Vite 8 or Next.js (Turbopack) | Depends on your SSR requirements |
| Vue 3 | Vite 8 | Officially recommended, best integration |
| Svelte/SvelteKit | Vite 8 | SvelteKit's official bundler |
| Next.js | Turbopack | Official bundler, best integration |
| Large legacy (webpack) | Rspack | You can reuse webpack.config |
| Remix | Vite 8 | Remix has been Vite-based since v3 |
| Astro | Vite 8 | Official 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
- Is the Node.js version 20.0.0 or later?
- Are
vite@^8.0.0and the related plugins on their latest versions? - Have you removed the
esbuildOptionssettings and replaced them with Rolldown-compatible ones? - Does CSS handling survive the switch to Lightning CSS without breaking?
- Are all Rollup plugins compatible with Vite 8?
- Do CommonJS modules convert correctly?
- Do the size and structure of the build output match your expectations?
- Does HMR work correctly for every file type?
- Does the CI/CD pipeline support a Vite 8 build?
- Are the source map settings appropriate for production?
- Are environment variables injected correctly?
- Have you run a build performance benchmark to confirm the improvement?
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
- Vite 8.0 Release Blog - official release notes and migration guide
- Rolldown GitHub Repository - source code and technical documentation for the Rust-based bundler
- Vite Official Documentation - Vite configuration, API, and plugin guides
- Evan You - "Vite and the Rolldown Vision" - a talk on the Vite/Rolldown architecture
- Turbopack vs Rspack vs Rolldown Benchmarks - cross-bundler performance comparison