writing/tutorial/2026/06
TutorialJun 19, 2026·26 min read

Rolldown + Vite 8: Migrate to the Rust-Powered Bundler in 2026

Vite 8 ships Rolldown as its default Rust bundler, replacing esbuild and Rollup. Learn how to migrate safely — from the gradual rolldown-vite path on Vite 7 to a full Vite 8 upgrade — and cut production build times by 10-30x.

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 vite package and configure package manager overrides for nested dependencies
  • Update deprecated config like manualChunks to the new advancedChunks API
  • Tune the native plugin engine with experimental.enableNativePlugin
  • Optimize third-party plugins with the withFilter helper
  • 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, and vite.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:

AspectVite 7 (esbuild + Rollup)Vite 8 (Rolldown)
Bundler engineTwo (dev + build differ)One unified Rust engine
Production build (Linear)46s6s
Beehiiv build timebaseline64% reduction
HMR latency (typical change)200-300ms20-30ms
JS/JSX transformsesbuildOxc
CSS minificationesbuild / optionalLightning CSS (default)
Benchmark vs Rollup1x10-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:

  1. 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.
  2. Gradual via rolldown-vite on 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 higher

If you use nvm, switch and pin:

nvm install 22
nvm use 22

An 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 build

Your 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 dev

The 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@latest

Remove 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 build

Thanks 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 plugins
  • false — 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 build

Then run your app from the production output and click through the critical paths:

pnpm run preview

Check for three things specifically:

  1. Build correctness — every route renders, assets load, no broken imports.
  2. Chunk layout — open dist/assets and confirm your advancedChunks groups produced the vendor splits you expect.
  3. 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


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.