Vite 8 Rolldown Migration: What Actually Breaks

Vite 8 replaces both esbuild and Rollup with Rolldown, a single Rust bundler, so the upgrade is mostly a config rename plus a handful of behaviour changes. build.rollupOptions becomes build.rolldownOptions, the esbuild option becomes oxc, the object form of manualChunks is removed, and the system and amd output formats are gone. Most projects need nothing else.

Vite 8.0 shipped on 12 March 2026. The latest release on npm as of 25 August 2026 is 8.2.2, and it requires Node 20.19+ or 22.12+.

What Vite 8 actually swapped out

Since Vite 2, every project has been running two bundlers. esbuild handled dependency pre-bundling and the TypeScript and JSX transforms in dev. Rollup produced the production build. That split is why “works in dev, breaks in build” was a recognisable category of Vite bug rather than a rare one.

Vite 8 collapses both into Rolldown. Oxc does the transforms and the JavaScript minification, Lightning CSS does the CSS minification, and Rolldown bundles in dev and production alike.

The figures Vite published come from companies that ran the beta in anger: Linear went from 46s to 6s on production builds, Beehiiv reported a 64% reduction, Ramp 57%, and Mercedes-Benz.io up to 38%. Vite’s own benchmarks put Rolldown at 10 to 30 times faster than Rollup. Install size goes the other way, roughly 15 MB larger than Vite 7, split between Lightning CSS (~10 MB) and Rolldown (~5 MB).

Which config keys break in a Vite 8 Rolldown migration

Start here, because this is where most upgrades stall. The renames:

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    // was build.rollupOptions
    rolldownOptions: {
      // was watch.chokidar
      watch: { watcher: {} },
    },
  },
  worker: {
    // was worker.rollupOptions
    rolldownOptions: {},
  },
  optimizeDeps: {
    // was optimizeDeps.esbuildOptions
    rolldownOptions: {},
  },
  // was the top-level `esbuild` option
  oxc: {},
})

build.commonjsOptions and build.dynamicImportVarsOptions.warnOnError are now no-ops rather than errors, which is the more annoying outcome: they sit in your config doing nothing and nobody notices for a year. resolve.alias[].customResolver is deprecated, so write a plugin with a resolveId hook instead.

Vite converts most of optimizeDeps.esbuildOptions for you: minify, treeShaking, define, loader, preserveSymlinks, resolveExtensions, mainFields, conditions, keepNames, platform, and partially plugins. The esbuild option maps jsxInject, include, exclude, jsx and define across to oxc. esbuild.supported has no equivalent at all.

The default browser target moved as well. build.target and 'baseline-widely-available' now resolve to Chrome 111, Edge 111, Firefox 114 and Safari 16.4, up from Chrome 107, Edge 107, Firefox 104 and Safari 16.0. Pin the target explicitly if you still support older browsers. Listing the same browser twice with different versions in build.target is now a hard error rather than silent last-one-wins.

Three additions are worth knowing while you have the file open. resolve.tsconfigPaths resolves TypeScript path aliases without a third-party plugin, server.forwardConsole pipes browser console output into your terminal, and devtools switches on the Vite Devtools integration.

Where did the esbuild transforms go?

Oxc does them now, and it is not a drop-in for every case.

The one that catches teams out: Oxc does not lower native decorators. If you use decorator syntax, you need a Babel or SWC plugin back in the pipeline. TypeScript’s emitDecoratorMetadata is handled natively by Vite 8 with no extra plugin, so work out which of the two you actually depend on before you add one. That split is the same syntax-only stripping trade-off Node makes with its built-in TypeScript support, and it has the same shape: anything needing type information at runtime is the exception.

Minification moved to the Oxc minifier. Property mangling is not supported, so mangleProps, reserveProps, mangleQuoted and mangleCache are out. You can set build.minify: 'esbuild', but it is deprecated and you have to install esbuild yourself now. Same story for CSS: Lightning CSS is the default, build.cssMinify: 'esbuild' reverts it, and expect CSS bundles to grow slightly. If the whole toolchain moving to Rust is a pattern you are tracking, our comparison of oxlint and Biome covers the linter half of it.

Two module-resolution changes are quieter and more likely to bite in production. Vite no longer sniffs file contents to choose between the browser and module fields; it respects resolve.mainFields order. And require calls for externalised modules stay as require calls rather than being rewritten to import. Add esmExternalRequirePlugin if you need the old rewrite.

CommonJS default imports are now resolved the same way in dev and build. That is the fix for the bug class above, and it will change behaviour for some packages. legacy.inconsistentCjsInterop: true restores the old inconsistency while you sort it out.

How do you replace manualChunks?

The object form of build.rollupOptions.output.manualChunks is removed. The function form still works but is deprecated. Rolldown’s replacement is output.codeSplitting, which is declarative and closer to what most people were writing by hand anyway:

export default defineConfig({
  build: {
    rolldownOptions: {
      output: {
        codeSplitting: {
          groups: [
            { name: 'react-vendor', test: /node_modules[\\/]react/, priority: 20 },
            { name: 'vendor', test: /node_modules/, priority: 10 },
            { name: 'common', minShareCount: 2, minSize: 10000, priority: 5 },
          ],
        },
      },
    },
  },
})

Each group takes name, test, priority, minSize, maxSize, minModuleSize, maxModuleSize, minShareCount and includeDependenciesRecursively, and the same fields can be set at the top level as fallbacks. Higher priority wins. Rolldown also exposes an older advancedChunks option; it is deprecated, and if you set both, advancedChunks is ignored.

Chunk splitting is one of the few build settings that turns up in real user metrics, so it is worth a proper look if you are already doing front-end performance work.

Will your plugins survive?

Mostly. Rolldown implements the Rollup plugin API and Vite runs a CI suite against the major plugins. Four hooks are unsupported: shouldTransformCachedModule, resolveImportMeta, renderDynamicImport and resolveFileUrl.

If you maintain a plugin, two changes matter. A transform hook that returns JavaScript for a non-JS input must now set moduleType: 'js' on the returned object. And comments are stripped before renderChunk rather than after, so anything reading or writing comments in that hook needs checking. parseAst and parseAstAsync are deprecated in favour of parseSync and parse, and build() throws a BundleError, typed as Error & { errors?: RolldownError[] }, instead of a raw error.

Scan for the edge cases too. Extglobs are unsupported. plugin-legacy’s ES5 transformation does not work. import.meta.url is undefined in UMD and IIFE output rather than polyfilled. Hooks marked parallel run sequentially. structuredClone(bundle) throws.

Is it worth going through rolldown-vite first?

For a small app, no. Change the version and run the build.

For anything with a long plugin list or a config someone tuned by hand, yes. rolldown-vite (7.3.1 on npm) is Vite 7 with Rolldown swapped in, so it isolates Rolldown-specific breakage from everything else Vite 8 changed. Fix what breaks there, then set vite to ^8.0.0 and drop the alias.

Should you turn on bundled dev mode?

Not yet, unless dev server startup is genuinely hurting. Vite 8.1, released 23 June 2026, added it behind experimental.bundledDev: true or the --experimental-bundle CLI flag. It lazily bundles only what the requested page needs and compiles the rest on demand.

The numbers are good. An app with 10,000 React components started about 15 times faster with roughly 10 times faster full reloads, and Linear reported cold start rendering up to 3 times faster with 10 times fewer network requests. The caveat is the one you would expect from an experimental flag: it currently focuses on the browser side and the basic plugins, third-party plugins may not work, and minor features may misbehave.

Upgrade, but do it on a branch. The renames are mechanical, the breakages are enumerated in Vite’s own migration guide, and the build-time reports from teams who ran the beta justify a day of config work. Run the production build, diff the output directory against your Vite 7 one, and check your CommonJS-heavy dependencies before anything else.

Two reasons to stay on 7.3.6. If you ship system or amd output, or you rely on plugin-legacy producing ES5, Vite 8 has no route for you. And if your codebase leans on native decorators, price in adding a Babel or SWC pass before you start, because that is the one thing Oxc will not do for you.

Need this built properly?

Whoooop Ltd has spent 15+ years building and maintaining web applications in TypeScript, React, Node.js and serverless — the same ground this post covers.

Get in touch