The JavaScript build toolchain is going native. On March 12, 2026, Vite 8 shipped with Rolldown — a Rust-based bundler — as its single default engine, replacing both esbuild and Rollup. Teams are reporting production builds dropping from 46 seconds to 6, and HMR latency falling from 200ms to under 30ms. This guide walks you through migrating an existing project safely, step by step.
What You Will Learn
By the end of this guide, you will know how to:
- Understand what Rolldown is and why Vite consolidated on a single Rust bundler
- Choose between the gradual (rolldown-vite on Vite 7) and direct (Vite 8) migration paths
- Alias the
vitepackage and configure package manager overrides for nested dependencies - Update deprecated config like
manualChunksto the newadvancedChunksAPI - Tune the native plugin engine with
experimental.enableNativePlugin - Optimize third-party plugins with the
withFilterhelper - Diagnose and fix the most common migration breakages
- Verify and benchmark the speed gains in your own project
Prerequisites
Before starting, make sure you have:
- Node.js 20.19+ or 22.12+ installed (the Vite 8 minimum)
- An existing Vite 6 or 7 project, or a willingness to scaffold a fresh one
- Familiarity with
package.json, your package manager, andvite.config.ts - A package manager: pnpm (recommended), npm, Yarn, or Bun
- Basic understanding of bundling concepts (chunks, tree-shaking, HMR)
Why Rolldown?
For most of its life, Vite has run on two bundlers. During development it used esbuild for dependency pre-bundling and transforms; for production builds it used Rollup. This split caused subtle dev-versus-build inconsistencies and meant the team maintained compatibility with two very different tools.
Rolldown ends that split. It is a single Rust-based bundler — built by the Vite team itself, on top of the Oxc toolchain — designed to be Rollup-compatible while running at native speed. Vite 8 ships it as the default, alongside Oxc for JavaScript and JSX transforms and Lightning CSS for stylesheet minification. The entire hot path is now Rust.
The payoff is speed and consistency:
| Aspect | Vite 7 (esbuild + Rollup) | Vite 8 (Rolldown) |
|---|---|---|
| Bundler engine | Two (dev + build differ) | One unified Rust engine |
| Production build (Linear) | 46s | 6s |
| Beehiiv build time | baseline | 64% reduction |
| HMR latency (typical change) | 200-300ms | 20-30ms |
| JS/JSX transforms | esbuild | Oxc |
| CSS minification | esbuild / optional | Lightning CSS (default) |
| Benchmark vs Rollup | 1x | 10-30x faster |
Note on install size. Vite 8 is roughly 15 MB larger on disk than Vite 7, because Rolldown (~5 MB) and Lightning CSS (~10 MB) ship as native dependencies. This is a one-time cost that pays for itself on the very first build.
Two Migration Paths
There are two ways onto Rolldown, and the right one depends on your project size:
- Direct upgrade to Vite 8 — best for small-to-medium apps and greenfield projects. A compatibility layer auto-converts most existing esbuild and Rollup options, so many projects upgrade with zero config changes.
- Gradual via
rolldown-viteon Vite 7 — best for large or plugin-heavy codebases. You stay on Vite 7's behavior but swap the bundler to Rolldown, isolating Rolldown-specific issues before you also absorb Vite 8's other changes.
We will cover both. Start with the path that matches your risk tolerance.
Step 1: Confirm Your Node.js Version
Vite 8 requires Node.js 20.19+ or 22.12+. Check first:
node --version
# Should print v20.19.x or higher, or v22.12.x or higherIf you use nvm, switch and pin:
nvm install 22
nvm use 22An outdated Node version is the single most common reason a Vite 8 install fails, so resolve this before touching dependencies.
Step 2 (Path A): Gradual Migration with rolldown-vite on Vite 7
This path keeps you on Vite 7 while swapping in the Rust bundler. The trick is to alias the vite package to rolldown-vite in package.json:
{
"devDependencies": {
"vite": "npm:rolldown-vite@7.1.0"
}
}Pin the version. Do not use @latest for rolldown-vite. It is an experimental release line and pinning protects you from surprise breaking changes between installs.
If Vite is a peer dependency of other packages (common in monorepos and plugin-heavy apps), an alias alone is not enough — you must override the nested resolution too. Use the syntax for your package manager:
npm:
{
"overrides": {
"vite": "npm:rolldown-vite@7.1.0"
}
}pnpm:
{
"pnpm": {
"overrides": {
"vite": "npm:rolldown-vite@7.1.0"
}
}
}Yarn:
{
"resolutions": {
"vite": "npm:rolldown-vite@7.1.0"
}
}Bun:
{
"overrides": {
"vite": "npm:rolldown-vite@7.1.0"
}
}Then reinstall and run your build:
pnpm install
pnpm run buildYour scripts, config file, and plugins stay the same. If the build succeeds and your app behaves identically, you have validated Rolldown against your codebase. Now you can move to Vite 8 with confidence.
Step 2 (Path B): Direct Upgrade to Vite 8
For a fresh project, scaffold directly:
pnpm create vite@latest my-app
cd my-app
pnpm install
pnpm run devThe generated project already targets Vite 8 with Rolldown as the default — there is nothing extra to enable.
To upgrade an existing project, bump Vite and your framework plugin:
pnpm add -D vite@8
# plus your framework plugin, for example:
pnpm add -D @vitejs/plugin-react@latestRemove any leftover vite alias from Path A (the npm:rolldown-vite@... line) before upgrading, so you install the real Vite 8 package rather than the standalone preview line.
Run the build and watch the timing:
pnpm run buildThanks to the built-in compatibility layer, Vite 8 auto-converts most existing esbuild and rollupOptions settings to their Rolldown and Oxc equivalents. A large share of projects need no config edits at all.
Step 3: Update Deprecated Config
The most common config change is manual chunking. Rollup's manualChunks function is replaced by Rolldown's declarative advancedChunks:
// Before — Rollup style
export default {
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (/\/react(?:-dom)?/.test(id)) {
return 'vendor'
}
}
}
}
}
}// After — Rolldown style
export default {
build: {
rollupOptions: {
output: {
advancedChunks: {
groups: [
{ name: 'vendor', test: /\/react(?:-dom)?/ }
]
}
}
}
}
}The new API is declarative: each group has a name and a test pattern. It is easier to reason about and lets Rolldown plan chunks ahead of time rather than calling back into JavaScript per module.
If you previously imported esbuild's transform helper directly, note that transformWithEsbuild is deprecated. Use the Oxc-based equivalent, transformWithOxc, instead.
Step 4: Tune the Native Plugin Engine
Vite 8's biggest speed lever is native plugins — Rolldown's built-in plugins that execute entirely in Rust, with no crossing of the JavaScript boundary. This is controlled by experimental.enableNativePlugin, which defaults to 'v2' in Vite 8:
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
experimental: {
// 'v2' is the Vite 8 default — full native plugins
enableNativePlugin: 'v2',
},
})If a plugin misbehaves under native mode, you can step the engine down as a temporary workaround:
'v2'— full native plugins (default)'v1'— earlier native plugin set'resolver'— disables some native pluginsfalse— disables all native plugins, closest to legacy behavior
Treat anything below 'v2' as a diagnostic step, not a destination. File the underlying issue and restore the default once it is fixed.
Step 5: Optimize Third-Party Plugins with withFilter
Plugins that run their transform or load hook on every module force a round-trip across the Rust-to-JavaScript boundary even for files they do not care about. The withFilter helper scopes a plugin to only the files it should touch, eliminating that overhead:
import { defineConfig, withFilter } from 'vite'
import svgr from 'vite-plugin-svgr'
export default defineConfig({
plugins: [
withFilter(
svgr({ /* options */ }),
{ load: { id: /\.svg\?react$/ } },
),
],
})Here svgr only fires for imports ending in .svg?react. Everything else stays in Rust. On large codebases this alone can shave seconds off a build.
Step 6: Handle Plugin-Author Concerns
If you maintain your own Vite plugin, two details matter under Rolldown.
First, when a load or transform hook returns code that should be treated as JavaScript, declare its module type explicitly:
import fs from 'node:fs/promises'
const plugin = {
name: 'txt-loader',
async load(id) {
if (id.endsWith('.txt')) {
const content = await fs.readFile(id, 'utf-8')
return {
code: `export default ${JSON.stringify(content)}`,
moduleType: 'js',
}
}
},
}Second, you can detect whether your plugin is running under Rolldown to branch behavior safely:
const plugin = {
name: 'my-plugin',
resolveId() {
if (this.meta.rolldownVersion) {
// Rolldown-specific path
} else {
// Classic Rollup path
}
},
}Or check the export at the top level:
import * as vite from 'vite'
if (vite.rolldownVersion) {
// rolldown-vite specific code
}Step 7: Verify and Benchmark
Migration is not done until you have measured the result. Time a clean production build before and after:
# Clean build with timing
rm -rf dist
time pnpm run buildThen run your app from the production output and click through the critical paths:
pnpm run previewCheck for three things specifically:
- Build correctness — every route renders, assets load, no broken imports.
- Chunk layout — open
dist/assetsand confirm youradvancedChunksgroups produced the vendor splits you expect. - HMR feel — run
pnpm run dev, edit a component, and confirm the update lands near-instantly.
If your numbers do not match the dramatic benchmarks, that is normal — gains scale with project size and the number of modules. Even a modest app should see a clear improvement.
Troubleshooting
The build fails with a plugin error. Step enableNativePlugin down to 'v1', then 'resolver', then false to isolate whether a native plugin is the cause. Report the issue to the vitejs/rolldown-vite repository with a minimal reproduction.
Missing CSS in production. Some plugins interact poorly with the native CSS pipeline. Confirm Lightning CSS is handling your styles and try disabling native plugins temporarily to confirm the source.
A peer dependency still pulls in classic Vite. Your override did not take. Double-check you used the correct key for your package manager (overrides, pnpm.overrides, or resolutions) and reinstall from a clean lockfile.
transformWithEsbuild is undefined. It is deprecated under Rolldown. Switch to transformWithOxc, or add esbuild as an explicit dependency if you genuinely still need it.
Install size jumped. Expected — Rolldown and Lightning CSS add about 15 MB. This is not a misconfiguration.
Next Steps
- Explore the new Vite 6 fundamentals guide to ground your config knowledge before migrating.
- Compare bundler approaches with our Rspack and Rsbuild tutorial — another Rust-powered option.
- Wire up fast unit tests with Vitest and React Testing Library, which shares Vite's transform pipeline.
- Once your builds are fast, revisit your CI to take advantage of the shorter build times.
Conclusion
Vite 8's switch to Rolldown is the most significant change to the project since version 2, and it lands without demanding much from most teams. The compatibility layer absorbs the bulk of the work, leaving you with a short checklist: confirm Node 20.19+/22.12+, optionally validate via rolldown-vite on Vite 7, migrate manualChunks to advancedChunks, scope heavy plugins with withFilter, and benchmark.
The reward is a build pipeline that is unified, written in Rust, and frequently an order of magnitude faster. For MENA teams shipping under tight CI budgets and slow network conditions, cutting a six-minute build to under twenty seconds is not a luxury — it is reclaimed developer time on every single push.