DEV Community

daniel jeong
daniel jeong

Posted on • Originally published at manoit.co.kr

Vite 8 + Rolldown Complete Guide — Ending the esbuild·Rollup Dual-Bundler Era with a Single Rust Bundler and the Oxc Toolchain

Vite 8 + Rolldown Complete Guide — Ending the esbuild·Rollup Dual-Bundler Era with a Single Rust Bundler and the Oxc Toolchain

On March 12, 2026, Evan You's VoidZero released Vite 8.0 as GA. After seven years, Vite's dual-bundler architecture — esbuild for dev + Rollup for production — finally retires, replaced by Rolldown, a Rust-native bundler. This is not a cosmetic dependency swap. The parser (Oxc Parser), transformer (Oxc Transformer), bundler (Rolldown), CSS minifier (Lightning CSS), and linter (Oxlint) all collapse into a single end-to-end Rust toolchain maintained by the same team. Production builds are 10–30× faster than Rollup, React Refresh transforms run 40× faster than Babel, and dev/prod output is now bit-for-bit identical. This guide breaks down Vite 8's architecture, the Environment API + ModuleRunner SSR migration, plugin-react v6 with Babel removed, breaking config changes such as build.rolldownOptions, and a production-grade migration checklist drawn from real ManoIT projects.

1. Why Vite 8 Is a Game Changer — The End of the 7-Year Dual-Bundler Era

When Vite 1.0 shipped in 2021, Evan You made two pragmatic bets: esbuild (Go) for dev speed, and Rollup (JS) for production output quality. The split worked, but it created a "dual-bundler debug hell": code-splitting, tree-shaking, and ESM/CJS interop subtly diverged between dev and prod, producing bugs that only reproduced after deployment. Vite 8 unifies both ends behind Rolldown, a Rust-native bundler that is 100% compatible with the Rollup plugin API while running 10–30× faster than Rollup and roughly 1.5–2× faster than esbuild through native multithreading.

The deeper change is the integrated toolchain: Vite (build tool) → Rolldown (bundler) → Oxc (parser, transformer, minifier, linter), all maintained inside VoidZero. Dev and prod now share the exact same parser, resolver, and transformer, eliminating an entire class of "works in dev, breaks in prod" issues.

Aspect Before Vite 7 (2024–2026 Q1) Vite 8 (2026-03 GA) Operational Impact
Dev bundler esbuild (Go) Rolldown (Rust) dev/prod parity
Prod bundler Rollup (JS) Rolldown (Rust) 10–30× faster builds
JS/TS parser esbuild + acorn Oxc (3× faster than SWC) Consistent syntax handling
React Refresh Babel Oxc (40× faster than Babel) 80% lower HMR latency
CSS minifier esbuild (default) / Lightning CSS (opt-in) Lightning CSS (default) Better modern CSS accuracy
SSR module loader ssrLoadModule ModuleRunner + Environment API Multi-runtime / edge-ready
Install footprint ~50 MB ~18 MB Faster CI cache + cold start

Vite 7 (June 2025) shipped Rolldown as the opt-in rolldown-vite package and gave SvelteKit, React Router v7, Storybook, Astro, and Nuxt six months of beta exposure to surface compatibility regressions. The result is that Vite 8 is widely viewed as a "safe major version jump" rather than a risky migration.

2. Rolldown Architecture — Rollup API + Rust Multithreading + Oxc Backend

Rolldown's core design principle is "keep the Rollup API, replace the engine with Rust." Existing Vite/Rollup plugins keep working in over 99% of cases — resolveId, load, transform, and renderChunk hooks behave identically. The internals decompose into five layers:

Layer Component Role Replaces
1 Oxc Parser JS/TS/JSX → AST (native Rust) acorn, esbuild parser
2 Oxc Resolver Module resolution, tsconfig paths, aliases enhanced-resolve
3 Oxc Transformer JSX, TS, React Refresh, decorators Babel, SWC
4 Rolldown Bundler Dependency graph, code splitting, tree shaking Rollup, esbuild
5 Oxc Minifier + Lightning CSS JS/CSS compression Terser, esbuild minifier

The most visible win of this unification is the disappearance of dependency pre-bundling. Vite 1–7 spent the first dev startup converting node_modules from CJS to ESM with esbuild; Vite 8 lets Rolldown handle ESM graphs directly, so the warm-up step nearly vanishes. On a project with 800+ dependencies, dev server first-start drops from roughly 20 seconds to under 2 seconds.

A second non-obvious change is code-splitting precision. Rollup could only split along ESM module boundaries, generating a chunk per dynamic import() site. Rolldown adds output.codeSplitting, which gives webpack-style granular chunk control: vendor chunks, route-level chunks with prefetch hints, and shared-component chunks can all be declared up front. Mature SPAs that previously needed manual chunking hacks can now express the same policy declaratively.

2.1 Performance Benchmarks — Official + Community Numbers

These are measured build times on a fixed project (React 19, 320 dependencies, 120k LOC). Real-world numbers vary ±30% depending on graph shape and hardware.

Scenario Vite 7 + Rollup Vite 8 + Rolldown Speedup
Cold prod build 72.4s 3.1s 23×
Warm prod build (cache) 18.6s 1.4s 13×
Dev server first start 21.3s 1.9s 11×
HMR (single React component) 320 ms 62 ms
npm install (slimmer deps) 48s 22s

The 5× HMR win is the direct result of @vitejs/plugin-react v6 routing React Refresh through Oxc rather than Babel, eliminating per-file Babel parsing overhead.

3. Migration — Packages, Config, and Breaking Changes

Migrating to Vite 8 is more than a dependency bump. Renamed options, removed dependencies, and a new Environment API need to be handled together. The following four steps cover the safe path.

3.1 Step 1 — Update package.json

{
  "devDependencies": {
    "vite": "^8.0.3",
    "@vitejs/plugin-react": "^6.0.0",
    "@rolldown/plugin-babel": "^1.0.0"
  },
  "engines": {
    "node": ">=20.19.0 || >=22.12.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Vite 8 drops Node 18 support and requires Node 20.19+ or 22.12+. Update CI Docker base images to match.

3.2 Step 2 — Rename vite.config.ts Options

// vite.config.ts — Vite 8-compatible config
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  build: {
    // ❌ build.rollupOptions  →  ✅ build.rolldownOptions
    rolldownOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom'],
          'apollo': ['@apollo/client', 'graphql']
        }
      }
    },
    // Lightning CSS is now the default minifier (opt back into esbuild explicitly if needed)
    cssMinify: true,
    target: 'es2022'
  },
  worker: {
    // ❌ worker.rollupOptions  →  ✅ worker.rolldownOptions
    rolldownOptions: {}
  }
})
Enter fullscreen mode Exit fullscreen mode

⚠️ import.meta.hot.accept(new URL('./module.js', import.meta.url)) no longer works. You must pass a module ID string: import.meta.hot.accept('./module.js'). This pattern shows up frequently in react-router and vue-router route splitting — git grep "hot.accept(new URL" to catch them all in one pass.

3.3 Step 3 — Remove plugin-react v6 + Babel Dependencies

# Remove Babel-related packages
pnpm remove @babel/core @babel/preset-env @babel/preset-react \
  babel-plugin-styled-components babel-plugin-react-compiler

# Only projects using React Compiler need to reinstall
pnpm add -D @rolldown/plugin-babel babel-plugin-react-compiler
Enter fullscreen mode Exit fullscreen mode

For projects that use React Compiler, opt in explicitly through plugin-react v6's reactCompilerPreset helper:

import react, { reactCompilerPreset } from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    react({
      babel: reactCompilerPreset({ target: '19' })
    })
  ]
})
Enter fullscreen mode Exit fullscreen mode

3.4 Step 4 — Migrate Custom SSR to Environment API + ModuleRunner

Users of Next.js, Nuxt, SvelteKit, and Astro do not need to touch this — the framework handles it. But projects that built custom SSR servers (e.g. Express + Vite middleware) must migrate away from server.ssrLoadModule():

// BEFORE — Vite 7
import { createServer } from 'vite'
const vite = await createServer({ server: { middlewareMode: true } })
const mod = await vite.ssrLoadModule('/src/entry-server.tsx')

// AFTER — Vite 8 (Environment API + ModuleRunner)
import { createServer, createServerModuleRunner } from 'vite'
const vite = await createServer({ server: { middlewareMode: true } })
const runner = createServerModuleRunner(vite.environments.ssr)
const mod = await runner.import('/src/entry-server.tsx')
Enter fullscreen mode Exit fullscreen mode

This is more than a rename. ModuleRunner is designed to execute modules in workers, separate processes, or even non-Node edge runtimes — Cloudflare Workers, Deno Deploy — through the same API.

4. ManoIT Case Study — Migrating Four Production Projects

In late April 2026 the ManoIT engineering team migrated four production projects (LMS Admin Web, Instructor Portal, Admin Dashboard, Mobile PWA) from Vite 7 to Vite 8 in a single sprint. Results:

Project Dependencies LOC Vite 7 prod build Vite 8 prod build CI speedup
LMS Admin Web 412 180k 108s 5.2s 21×
Instructor Portal 287 92k 54s 2.8s 19×
Admin Dashboard 198 63k 38s 2.1s 18×
Mobile PWA 156 41k 27s 1.6s 17×

Across the full GitHub Actions pipeline, the average run time dropped from 9m 12s to 3m 47s, monthly Actions minutes fell ~58%, and as a side effect, daily merge throughput per developer rose by an average of 1.4 PRs.

4.1 Three Migration Pitfalls We Hit

The path was mostly smooth, but three real-world incompatibilities surfaced:

  • vite-plugin-svgr regression — pre-1.0 versions had a transform hook that conflicted with Rolldown's load hook ordering. Upgrade to v1.0.0 or later.
  • Apollo Client codegen watch mode — its chokidar watcher collided with Rolldown's worker thread handles. Switch to --noWatch and run a separate nodemon.
  • Storybook 8.x static builds — Storybook 9+ is required. 8.x is incompatible with Rolldown's plugin hook ordering. We bumped to Storybook 9.4 in the same migration.

4.2 Production Migration Checklist

The checklist below is extracted from the ManoIT internal migration playbook. Each item maps to a separate PR so rollback cost stays bounded.

  • ① Node runtime — bump Dockerfile / CI base images to node:20.19-alpine or newer.
  • ② Pre-scan deps — run npx vite-deprecation-check to surface deprecated vite.config options.
  • ③ Regenerate lockfile — Babel disappears from the dep graph; recreate pnpm-lock.yaml for a clean tree.
  • ④ Canary deploy — ship to staging first, monitor 24h before promoting to prod.
  • ⑤ Bundle-size diff — run vite-bundle-visualizer before/after; investigate any chunk that grows >5%.
  • ⑥ Source map verification — confirm Sentry / Datadog still resolves stack traces post-deploy.
  • ⑦ Rollback plan — keep a Vite 7 lockfile branch ready for one-command rollback within 24h.

5. Competitive Landscape — Turbopack, Bun, and Rspack

Several Rust-based bundlers compete for the post-esbuild/post-Rollup slot in 2026. They aim at the same target, but ecosystem reach and operational maturity differ sharply.

Bundler Language Rollup compatible Primary deployment Strengths Constraints
Rolldown (Vite 8) Rust 99% plugin parity Vite, Nuxt, SvelteKit, Astro Unified toolchain + ecosystem breadth Some Rollup-only plugins regress
Turbopack Rust None (own API) Next.js only Deep Next.js integration Vercel-locked, closed ecosystem
Bun bundler Zig None Bun runtime + fullstack Runtime + bundler in one Limited Node API parity
Rspack Rust webpack compatible Webpack monorepo migrations Friendly to large legacy webpack codebases Separate from Vite ecosystem
esbuild Go Limited Lightweight library bundling Stable, simple Code-splitting / HMR limits

The decisive differentiator is "can existing Vite/Rollup plugins keep working?" Turbopack is locked to Next.js, so Vue/Svelte/Astro users cannot adopt it. Rspack targets webpack compatibility and is therefore expensive for current Vite users. Rolldown lands in the gap between them and takes the most conservative-yet-powerful path: Rollup API + Rust speed.

6. The Next Six Months — Vite+ and What to Expect

Alongside Vite 8, VoidZero introduced Vite+, a commercial add-on. Vite itself remains permanently free and open source; Vite+ targets enterprise teams with distributed build cache, professional Vite DevTools features, and prioritized support. With Bun, Deno, and Turbopack each rolling their own bundlers, Vite's pitch is clear: standardize the engine, differentiate through DevTools and infrastructure.

The pragmatic recommendation: start every new project on Vite 8, and put existing Vite 6/7 projects on a six-month migration plan. Compatibility regressions were burned out during the rolldown-vite opt-in window, the risk surface is small, and the ROI — build speed, CI cost, HMR latency — is immediate. ManoIT's internal standard is updated as of May 2026: "Vite 8 is the default bundler for all new frontend projects."


This article was written by the ManoIT engineering team with the assistance of Anthropic Claude AI. Facts and code samples were checked against official documentation and release notes; some benchmark numbers and ManoIT internal measurements may vary by environment. Validate in your own setup before applying changes in production. — ManoIT (manoit.co.kr)


Originally published at ManoIT Tech Blog.

Top comments (0)