Why Vite 8 Cut Build Times by Up to 97%: How Rolldown Replaced the esbuild·Rollup Dual-Bundler Architecture
If you've done frontend development, you've probably run into this situation at least once. Everything works fine locally, but after pushing a production build, something quietly breaks. I spent hours thinking it was a code issue, only to discover it was a behavioral inconsistency caused by the development server and production build using different bundlers.
This was actually an unavoidable tradeoff in Vite's design. When Vite first appeared, esbuild — written in Go — was the best option for a fast dev server, while Rollup was far more mature for production optimizations like tree-shaking and chunk splitting. Combining the two tools was the best choice at the time, but it also planted the seeds of "environment-dependent bugs."
Vite 8, officially released on March 12, 2026, finally resolves this architecture. A single Rolldown — written in Rust — now handles the entire development and production pipeline, and the result was a 97% reduction in build times in Plaid's real-world monorepo environment. By the end of this article, you'll understand why Rolldown is fast, where you might get stuck during an upgrade, and have a checklist ready to evaluate for your team right now.
Core Concepts
Why Vite's Dual-Bundler Architecture Was a Problem
Here's a simple summary of Vite 7's architecture:
| Environment | Bundler | Role |
|---|---|---|
| Dev server | esbuild (Go) | Dependency pre-bundling, fast HMR |
| Production build | Rollup (JavaScript) | Optimization, tree-shaking, chunk splitting |
With two bundlers each interpreting modules in their own way, results could differ in edge cases. Plugins had to account for two kinds of APIs, and subtle issues like source map consistency also originated here. It was a breeding ground for environment-dependent bugs.
Structurally fixing this problem required merging the two bundlers into one, with two options: reuse existing JavaScript-based tools, or build from scratch in Rust. The VoidZero team chose the latter.
Why Rolldown Is Fast
Rolldown is a JavaScript/TypeScript bundler developed in Rust by the VoidZero team. As the name suggests, it maintains API compatibility with Rollup while replacing the entire execution engine with Rust — that's the key.
There are three reasons for the speed difference.
First, there is no GC (garbage collection). Bundlers based on JavaScript runtimes experience stop-the-world pauses when GC kicks in while processing large module graphs. Rust manages memory through its ownership model, so these pauses don't exist.
Second, true multi-threaded parallel processing is possible. Without Node.js's single-threaded constraints, the entire module graph can be processed in parallel. This is why the official synthetic benchmark with 19,000 modules shows Rolldown (1.61s) running about 25x faster than Rollup (40.10s).
Third, a single Oxc pipeline. The same Rust-based compiler, Oxc, handles all stages of parsing, transformation, and minification. In the traditional toolchain, Babel would transform, then esbuild would re-parse and minify — multiple tools re-parsing the AST — and that redundant cost is eliminated.
What is Oxc (Oxidation Compiler)? It's an integrated JavaScript parser, transpiler, and minifier written in Rust. It handles in a single Rust binary what Babel (transformation) and esbuild (minification) each handled separately in the traditional JavaScript toolchain.
A Vertically Integrated Toolchain from a Single Team
Another thing worth noting in Vite 8 is the structural integration.
VoidZero organization
├── Vite — build tool, dev server
├── Rolldown — bundler engine (replaces esbuild + Rollup)
└── Oxc — parser/transpiler/minifier (replaces Babel + esbuild transform role)In the open-source ecosystem, combining tools made by different teams frequently causes version mismatch problems where "A is supported but B isn't yet." When three tools evolve together under the same organization, that problem is internalized. The fact that Rolldown 1.0 stable was released simultaneously with Vite 8 is thanks to this structure.
Now let's look at how this architecture applies in real projects.
Practical Application
Example 1: Upgrading from Vite 7 to Vite 8
Honestly, if you're using a standard Vite configuration, the upgrade itself is much simpler than you'd expect. That's because Vite 8 includes a built-in compatibility layer that automatically converts existing rollupOptions and esbuild configurations.
# Upgrade the package
pnpm add -D vite@8
# If you're using React, it's a good idea to upgrade the plugin too
pnpm add -D @vitejs/plugin-react@latest
# For Vue
pnpm add -D @vitejs/plugin-vue@latestThere are framework plugins like @vitejs/plugin-react that need to be updated alongside the Vite version. If you see peer dependency warnings after upgrading, check this first.
// vite.config.ts — existing config works as-is (compatibility layer handles it)
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
// existing rollupOptions can remain unchanged — Rolldown converts internally
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
},
},
},
},
})// vite.config.ts — when explicitly switching to Vite 8 native config
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
// switching to rolldownOptions enables Rolldown-native features
rolldownOptions: {
output: {
// unlike manualChunks, advancedChunks supports pattern-based grouping
// and can be used alongside size-based auto-splitting for greater flexibility
advancedChunks: {
groups: [
{ name: 'react-core', test: /node_modules\/(react|react-dom)/ },
{ name: 'vendor', test: /node_modules/ },
],
},
},
},
},
})| Config key | Vite 7 | Vite 8 | Notes |
|---|---|---|---|
build.rollupOptions |
Passed directly to Rollup | Auto-converted via compatibility layer | Existing config works as-is |
build.rolldownOptions |
N/A | Rolldown native config | Use when leveraging new features |
optimizeDeps.esbuildOptions |
Passed directly to esbuild | Most options auto-converted, some excluded | See caveats below |
Example 2: Plugin Migration Checkpoints
Before checking custom plugins, there's something to verify first. Look through vite-plugin-* and rollup-plugin-* entries in your package.json's devDependencies, and check the npm page or GitHub for each plugin to confirm Vite 8 support. Skipping this step can result in a build that succeeds but behaves strangely at runtime.
Next, if you have custom plugins that directly use transformWithEsbuild, those need attention. There are two options:
// Old approach — direct esbuild dependency
import { transformWithEsbuild } from 'vite'
const myPlugin = {
name: 'my-plugin',
async transform(code, id) {
if (id.endsWith('.tsx')) {
const result = await transformWithEsbuild(code, id)
return result
}
},
}// Option A — migrate to Oxc-based approach (recommended direction for Vite 8)
// Check the official Vite 8 migration guide for the latest API name
import { transformWithOxc } from 'vite'
const myPlugin = {
name: 'my-plugin',
async transform(code, id) {
if (id.endsWith('.tsx')) {
const result = await transformWithOxc(code, id)
return result
}
},
}// Option B — install esbuild separately and keep it (gradual migration strategy)
import { transform } from 'esbuild'
const myPlugin = {
name: 'my-plugin',
async transform(code, id) {
if (id.endsWith('.tsx')) {
const result = await transform(code, { loader: 'tsx' })
return { code: result.code }
}
},
}Here's one real-world pitfall worth highlighting. If you've been using mangleProps to optimize bundle size, the Oxc minifier does not support this option.
// This config is silently ignored in Vite 8
export default defineConfig({
build: {
minify: 'oxc', // Vite 8 default
esbuildOptions: {
// mangleProps, mangleCache are not supported by Oxc and will not work
// As a result, internal property names won't be shortened,
// and your bundle size will be larger than expected
mangleProps: /^_/,
mangleCache: {},
},
},
})Because it's silently ignored rather than throwing an error or warning, it can take time to find the cause. If you depend on this feature, you'll need to configure a separate post-processing plugin.
What is Property Mangling? It's an optimization technique that renames things like
myLongPropertyNametoato reduce bundle size. It's primarily used in libraries that don't expose internal property names externally. esbuild'smanglePropsoption handled this, but the Oxc minifier currently does not support it.
Pros and Cons
Advantages
| Item | Details |
|---|---|
| Build speed | 25x faster than Rollup on a synthetic benchmark of 19,000 modules; 97% reduction in Plaid's real-world monorepo |
| Dev/production consistency | Structural elimination of environment-dependent bugs by using the same bundler |
| Single plugin API | Single API instead of the dual esbuild + Rollup ecosystem, reducing compatibility issues |
| Source map consistency | Reduced dev/production source map discrepancies with a single transform pipeline |
| Persistent caching | Module-level persistent cache support for improved incremental builds |
| Module Federation | Officially usable in Vite projects with Rolldown support (useful for micro-frontend setups) |
Disadvantages and Caveats
| Item | Details |
|---|---|
| Increased install size | ~+15MB over previous (lightningcss +10MB, Rolldown binary +5MB) |
| No property mangling support | Oxc does not support mangleProps, mangleCache, etc. |
| Custom plugin validation required | Plugins depending on esbuild internal APIs need behavior verification |
| Complex SSR config | Edge cases exist when combining SSR builds with custom chunk strategies |
Mitigations:
- Increased install size — If your CI cache layers are well-organized, the impact is minimal. With Docker, layer separation can absorb this comfortably.
- Property mangling — Consider using a separate post-processing plugin or terser.
- Custom plugins — It's recommended to verify in a staging environment before deploying to production.
- SSR edge cases — Things that worked fine locally can break in staging. Always validate against a staging environment that closely resembles your actual server environment.
Most Common Mistakes in Practice
- Deploying directly to production without checking third-party plugin compatibility — If you don't review your
vite-plugin-*list first, you may end up with a build that succeeds but behaves strangely at runtime. - Migrating
mangleProps-dependent config as-is — If you're unaware that Oxc silently ignores this option, it can be hard to track down the cause when your bundle size comes out larger than expected. - Validating SSR only locally — Even if the client build is fine, parts where SSR and custom chunk strategies intersect can behave differently than expected in an actual server environment.
Closing Thoughts
As we've seen, the core of Vite 8 is not simply a speed improvement, but an architectural-level resolution of the dual-bundler structure where development and production used different bundlers. The result is the structural elimination of environment-dependent bugs, with performance gains as a bonus.
For projects using standard Vite configurations, the transition will go much more smoothly than you'd think. The first things to check in your project right now are whether your custom plugins depend on esbuild, and whether you're using mangleProps. If neither applies, you can likely upgrade almost immediately.
3 steps you can start right now:
- Check the
vite-plugin-*androllup-plugin-*entries in yourpackage.json'sdevDependencies, and look for any usage ofmangleProps. - Create a separate branch and try upgrading with
pnpm add -D vite@8. Run bothpnpm buildandpnpm dev, and if you have SSR, validate the server build as well. - Use
time pnpm buildto compare build times before and after — you can see the real-world impact for yourself. Even if it's not as dramatic as Plaid's 97% or Linear's 87%, there's a good chance you'll see a noticeable difference depending on your project's size.
References
- Vite 8.0 is out! | vite.dev
- Vite 8 Beta: The Rolldown-powered Vite | vite.dev
- Announcing Rolldown 1.0 | VoidZero
- Migration from v7 | Vite Official Migration Guide
- How PLAID Cut Build Times by 97% Migrating From Rollup To Rolldown | VoidZero
- Vite team claims 10-30x faster builds with Rolldown | The Register
- Vite 8, Rolldown, and Oxc: Rust Is Taking Over the JavaScript Toolchain | DEV Community
- Announcing Rolldown 1.0 RC | VoidZero
- VoidZero's Rolldown Library: Rollup Compatible API with the Speed of Rust | InfoQ