---
title: 'Vite 8 Build Fails After Upgrade: Causes and Fixes (2026)'
description: Vite 8 swapped Rollup and esbuild for Rolldown and Oxc. Here is why your build fails after upgrading, what was actually removed, and the fix for each error.
url: https://nerdleveltech.com/vite-8-build-fails-after-upgrade
language: en
date: '2026-09-03'
category: frontend
tags:
  - vite 8
  - rolldown
  - oxc
  - lightning css
  - build tools
  - frontend
  - javascript
  - migration
markdown_url: https://nerdleveltech.com/vite-8-build-fails-after-upgrade.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/vite-8-build-fails-after-upgrade'
terms: https://nerdleveltech.com/llms.txt
---

# Vite 8 Build Fails After Upgrade: Causes and Fixes (2026)

Vite 8 replaced Rollup and esbuild with Rolldown and Oxc, so builds that worked on Vite 7 can fail on behaviour changes rather than config errors. The documented causes include Lightning CSS minification, non-ESM top-level await, unified CommonJS interop, unlowered native decorators, and a short list of removed options.

## TL;DR

Vite 8 shipped stable on **March 12, 2026** and replaced both of Vite's bundlers — esbuild for development transforms, Rollup for production — with a single Rust bundler, Rolldown, plus the Oxc toolchain for transforms and minification.[^announce] Vite ships a compatibility layer: `build.rollupOptions` is a still-working deprecated **alias**, and the `esbuild` and `optimizeDeps.esbuildOptions` options are **converted** to their Oxc and Rolldown equivalents automatically.[^build-options][^migration]

What breaks instead is the long tail: two new default minifiers, a stricter top-level-`await` rule, a unified CommonJS `default` import rule, native decorators that Oxc does not lower, and a short list of genuinely removed options. This page walks each documented failure to its fix, verified against Vite **8.2.2**.[^npm][^migration]

## What you'll learn

- Why the bundler swap, not your config, is the root cause of most Vite 8 build failures
- Whether `build.rollupOptions` was renamed, removed, or neither — and what happens if you set both
- Which options were removed outright, as opposed to deprecated, no-op, or unsupported by the new tools
- How Lightning CSS minification silently changes or rejects your CSS, and four ways to opt out
- What `The "TopLevelAwait" is not supported with the "iife" output format` means, and why `worker.format` is usually the cause
- Why `resolve.alias` can stop working in a programmatic `build()` call, and the trap in the workaround
- Why a `default` import from a CommonJS package now returns an object
- Why native decorators stop compiling while your `experimentalDecorators` code keeps working
- What the evidence actually says about Yarn PnP, in both directions
- Why your bundle targets newer browsers than it did on Vite 7
- Whether you still need `esbuild` installed
- How to actually find the one plugin that is breaking your build
- Why a patch release of Vite 8 can break a build that worked yesterday
- Why it builds locally and fails in CI, which is a different problem from all of the above
- The gradual-migration path, and a real rollback procedure
- What the speed gain costs, and who measured it

## Why does my build fail after upgrading to Vite 8?

Because Vite 8 changed bundlers, not because your config is wrong. Vite used esbuild for dev-time transforms, dependency pre-bundling and minification, and Rollup for production bundling; Vite 8 uses Rolldown for bundling and dependency optimization, and Oxc for JavaScript transforms and minification.[^migration] Anything that depended on Rollup's or esbuild's specific behaviour — an output-format rule, a plugin hook, a minifier assumption — is now running through different code.

Vite's own framing is that this is "the most significant architectural change since Vite 2."[^announce] That is a useful expectation to set: a failing build after the upgrade is usually a genuine behavioural difference, not a missing rename.

The practical consequence is that the fix rarely lives in a search for your error string. It lives in one of seven buckets:

| Bucket | Typical symptom | Where it is covered below |
|---|---|---|
| Removed option | A setting that no longer applies, with no equivalent in place | Which options were removed |
| New default minifier | CSS rejected at build time, or visually broken output with no error | Why your CSS breaks |
| Output-format constraint | Build error naming a file inside `node_modules` | Top-level await |
| Unsupported transform | Decorators, or ES5 output, that used to compile | Decorators |
| Resolution differences | "failed to resolve import" for a path that used to work | Aliases, and patch-release regressions |
| Runtime-only change | Green build, wrong behaviour in the browser or on the server | CommonJS interop |
| Environment, not code | Builds locally, fails only in CI | Why it fails in CI |

Work down that table before touching your config. Note the "New default minifier" and "Runtime-only change" rows especially: those two failures can produce no build error at all, which is why searching for an error string finds nothing.

## Do I need to rename `build.rollupOptions` to `build.rolldownOptions`?

No. `build.rollupOptions` still works in Vite 8. The config reference documents it as "an alias of `build.rolldownOptions`" and marks it **Deprecated**, not removed.[^build-options] `worker.rollupOptions` is documented identically.[^worker-options] Separately, `optimizeDeps.esbuildOptions` and the top-level `esbuild` option are *converted*: the guide publishes mapping tables and Vite applies them for you, though two entries are not mechanical — `esbuild.banner` and `esbuild.footer` map to "custom plugin using transform hook", and `esbuildOptions.plugins` is marked "(partial support)".[^migration]

This is worth stating plainly because a good deal of the 2026 write-ups about Vite 8 lead with "`rollupOptions` was renamed to `rolldownOptions`" as though a rename were the required first migration step. It is a recommended cleanup, not a fix.

Those write-ups are not inventing the word, either. Vite's own migration guide lists the option under "Other Related Deprecations" as "`build.rollupOptions`: renamed to `build.rolldownOptions`", while the configuration reference for the same version calls it an alias that is merely deprecated.[^migration][^build-options] Two pages in the same docs set, two framings. The config reference is the one that describes runtime behaviour.

**The one thing that will genuinely bite you is setting both.** If a config carries `build.rollupOptions` *and* `build.rolldownOptions`, the docs say nothing about precedence — but Vite's source does, in a comment above the line that implements it: "if both rollupOptions and rolldownOptions are present, ignore rollupOptions and use rolldownOptions".[^vite-src] They are not merged. The `rollupOptions` object is discarded whole. That matters because the alias workaround later on this page asks you to add a `build.rolldownOptions` block — if you already have externals, chunking or output settings under `build.rollupOptions`, adding that block silently drops all of them. Migrate the whole object, not part of it.

There is a related warning that only fires for plugin-supplied config, not your own: "Both `rollupOptions` and `rolldownOptions` were specified by … plugin. `rollupOptions` specified by that plugin will be ignored."[^vite-src]

One nuance on deprecation warnings, because it is the opposite of what you would guess. Vite's runtime deprecation warning for the `rollupOptions` name is scoped in source to `optimizeDeps` and `ssr.optimizeDeps` only — `build` and `worker` are not in that set.[^vite-src] So `optimizeDeps.rollupOptions` warns and `build.rollupOptions` does not. When a warning does fire and the option was set by a dependency rather than by you, Vite's message names the escape hatch: "Set VITE_DEPRECATION_TRACE=1 to see where it is called", which switches the log from `console.warn` to `console.trace` so you get the call site.[^vite-src] That environment variable is not documented on vite.dev; it is visible in the source.

The other day-one move is inspecting what the compatibility layer produced. Note that this needs to be a *registered* plugin — a bare object assigned to a variable does nothing:

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

export default defineConfig({
  plugins: [
    {
      name: 'log-config',
      configResolved(config) {
        console.dir(config.optimizeDeps.rolldownOptions, { depth: null })
        console.dir(config.oxc, { depth: null })
      },
    },
  ],
})
```

Both property paths come from the migration guide's own `configResolved` examples.[^migration] Two caveats before you read the output: `config.optimizeDeps.rolldownOptions` will be `undefined` if you never set `optimizeDeps.esbuildOptions` and no plugin did either, which is normal rather than a bug; and `console.dir` with `depth: null` is deliberate, because `console.log` on a resolved config prints `[Function]` for exactly the entries you most want to see.

## Which Vite 7 options were actually removed in Vite 8?

Fewer than the migration chatter suggests, and the guide sorts them into categories that are worth keeping distinct, because only the first will leave you with no equivalent at all.[^migration]

**Genuinely removed.**

- `build.rollupOptions.watch.chokidar` — use `build.rolldownOptions.watch.watcher`.
- The **object form** of `output.manualChunks`. The function form is deprecated but still present.
- Passing a URL to `import.meta.hot.accept` — pass an id instead.

**Not supported by Rolldown**, which the guide lists separately under "Missing support by Rolldown": the `system` and `amd` output formats, and the `shouldTransformCachedModule`, `resolveImportMeta`, `renderDynamicImport` and `resolveFileUrl` hooks.[^migration]

**Not supported by Oxc**, where the option name survives but the capability does not: `esbuild.supported`, and esbuild's property-mangling options (`mangleProps`, `reserveProps`, `mangleQuoted`, `mangleCache`).[^migration]

**Now a no-op** — the option still parses and does nothing: `build.commonjsOptions`, and `build.dynamicImportVarsOptions.warnOnError`.[^migration] This is the category to check first, because a no-op cannot announce itself. If you were relying on `commonjsOptions` to shape CJS handling, that behaviour is gone.

One item that is often filed as a removal but is really new input validation: passing the same browser with multiple versions of it to `build.target` now errors, where esbuild "selects the latest version of it, which was probably not what you intended."[^migration]

The `manualChunks` change is the one worth checking first, because the object form is the shape that "split your vendor bundle" guidance has taught for years:

```js
// Vite 7 — the object form is no longer supported in Vite 8
build: {
  rollupOptions: {
    output: {
      manualChunks: { vendor: ['react', 'react-dom'] },
    },
  },
}
```

The replacement is Rolldown's `codeSplitting`, at `build.rolldownOptions.output.codeSplitting`. It is not a rename — the shape is different. A group's `test` is optional and accepts a string, a regular expression, or a predicate function over the module id, rather than a list of package names, so the translation is a rewrite rather than a copy:[^rolldown-cs]

```js
// Vite 8
build: {
  rolldownOptions: {
    output: {
      codeSplitting: {
        groups: [
          { name: 'vendor', test: /node_modules[\\/]react(-dom)?[\\/]/ },
        ],
      },
    },
  },
}
```

Two documented behaviours to expect from that option before you ship it. Rolldown's own guidance: "If you used manual code splitting with groups, rolldown will forcefully generate a `runtime.js` chunk to ensure that the runtime code is always executed before any other chunks", and "When a module is captured by a group, Rolldown will try to capture its dependencies recursively without considering constraints."[^rolldown-cs] Both change your chunk graph in ways a naïve translation will not predict, so diff the emitted chunk list rather than trusting the config to be equivalent.

## Why does my CSS break or fail to minify in Vite 8?

Because `build.cssMinify` now defaults to `'lightningcss'` instead of esbuild — with the one exception that it is `false` when `build.minify` is disabled for the client build.[^build-options] Lightning CSS is stricter about syntax it does not recognise and more opinionated about what it rewrites, and it produces two distinct failure modes.

**The loud one is a build error on modern CSS.** A tracked issue opened on March 17, 2026 — "Default CSS minifier in Vite@8 blocks some progressive modern CSS features" — reports the build failing because the `@scope` rule is rejected during minification, along with problems for `::scroll-marker`, `::scroll-marker-group`, `::scroll-button()`, `:target-current`, and `::search-text`.[^css21911] It is labelled as an upstream bug and was still open at the time of writing. The reporter's stated workaround: "Switching the CSS minifier back to `esbuild` resolves the issue."[^css21911]

**The quiet one is worse.** A report from June 9, 2026, on Vite 8.0.16 with lightningcss 1.32.0 describes the unprefixed `backdrop-filter` declaration being dropped from the output, leaving only `-webkit-backdrop-filter` and breaking glass and blur effects — with no error at any point.[^css22649] It was closed as a duplicate of an earlier issue whose title states the precise trigger: "`backdrop-filter` before `-webkit-backdrop-filter` is dropped with `cssMinify: 'lightningcss'`".[^css21954] So the ordering of the two declarations is what matters, and that earlier issue — opened November 20, 2025 and also labelled an upstream bug — was still **open** at the time of writing.[^css21954] This one is not fixed; it is tracked.

You have four levers, from lightest to heaviest. The first three are worth trying before the last one, which is the only one that changes your whole CSS pipeline:

1. **Override the transitive Lightning CSS version.** Both of these are upstream bugs in Lightning CSS rather than in Vite, so an `overrides` (npm), `resolutions` (Yarn) or `pnpm.overrides` entry pinning a version that behaves is the narrowest possible fix.
2. **Set `build.cssTarget`.** When `cssMinify` is `'lightningcss'`, `build.cssTarget` takes precedence over `css.lightningcss.targets` for the minification step, which is the knob for syntax-lowering behaviour — including the case where your CSS bundle *grew* after upgrading.[^build-options]
3. **Disable CSS minification entirely** with `build.cssMinify: false` as a triage step, to confirm the minifier is the culprit before you change anything permanent.
4. **Switch the minifier back to esbuild**, which reverts the whole CSS pipeline and is the option both issue reporters used:

```js
// vite.config.js
export default defineConfig({
  build: { cssMinify: 'esbuild' },
})
```

```bash
npm add -D "esbuild@^0.27.0 || ^0.28.0"
```

The config reference states the dependency requirement directly: "esbuild must be installed when it is set to `'esbuild'`."[^build-options] Pin it to the range Vite declares as its peer dependency rather than installing whatever is latest, or a future esbuild major will fail a strict install.[^npm]

The migration guide also notes that Lightning CSS "supports better syntax lowering and your CSS bundle size might increase slightly", so a somewhat larger CSS file after upgrading is expected — but if the increase is large, `build.cssTarget` is the lever, not resignation.[^migration]

## What does `The "TopLevelAwait" is not supported with the "iife" output format` mean?

It means something in your bundle uses top-level `await`, and the output format cannot express it. The constraint is not specific to IIFE: Rolldown's rule is that "If your input contains TLA, it could only be bundled and emitted with `esm` format."[^rolldown-tla] Every non-ESM format is affected, so switching from IIFE to UMD or CJS will fail identically.

So the real question is where a non-ESM output format entered your build. There are three common answers, and only one of them is usually deliberate.

**Workers.** `worker.format` defaults to `'iife'`, and its only other allowed value is `'es'`.[^worker-options] A dependency with top-level `await` that is fine in your main bundle can therefore blow up the worker bundle alone. That makes this the cheapest thing to try:

```js
// vite.config.js
export default defineConfig({
  worker: { format: 'es' },
})
```

Check your browser support before shipping it — module-worker support is why the default is still `'iife'`.

**Library builds.** Vite's default `build.lib.formats` are "`['es', 'umd']`, or `['es', 'cjs']`, if multiple entries are used" — so a library build emits a non-ESM bundle whether or not you asked for one.[^build-options] Expect the error to name whichever of those formats applies rather than `"iife"`; the constraint is the same, since Rolldown emits top-level `await` only in `esm`.[^rolldown-tla]

**Neither of the above.** A report filed on April 23, 2026 is exactly this case: a plain `vite build` that succeeded on Vite **8.0.5** fails on **8.0.10**, with the error pointing at `node_modules/@novnc/novnc/lib/util/browser.js` and no worker in the reproduction at all.[^tla] Two things are worth taking from it. The file named in the error is a dependency, not your source, so searching your own code will waste time. And the failure moved between patch releases, which means "it worked last week" is not evidence that your config is fine — see the patch-release section below.

If the format is not yours to change — a library that must keep publishing `umd` or `iife` for `<script>`-tag consumers — the remaining options are to remove the top-level `await` from the dependency graph, or to build as ESM and downlevel afterwards. Pinning is a last resort and worth naming precisely: the cited report's last good version was 8.0.5, which is a long way behind 8.2.2.

## Why did `resolve.alias` stop working in my programmatic `build()` call?

Because in at least one build path the alias never reaches Rolldown's resolver. The narrow, verified case is `build()` invoked from a Cypress preprocessor. A discussion opened on May 1, 2026 against Vite 8.0.10 reports it with the error `Rolldown failed to resolve import "@/model/Alert.model"`, and the follow-up bug report filed on May 8 against 8.0.11 reproduces it as `Rolldown failed to resolve import "@/foo.js"`.[^alias][^alias-issue]

Scope matters here, because the issue titles say "library mode" and the reproduction says something narrower. The reporter's own repro shows the *same function* succeeding when run as a standalone script and throwing only under the Cypress preprocessor.[^alias-issue] This is not "programmatic builds are broken," and it is not "library mode is broken."

The workaround is to declare the alias where Rolldown will see it as well:

```js
import path from 'node:path'

await build({
  configFile: false,
  resolve: { alias: { '@': path.resolve('./src') } },
  build: {
    rolldownOptions: {
      resolve: { alias: { '@': path.resolve('./src') } },
    },
    lib: { entry: filePath, formats: ['es'] },
  },
})
```

The reporter states that moving `resolve.alias` under `build.rolldownOptions` "causes it to work in both cases."[^alias-issue] Note the absolute path: the reproduction resolves it with `path.resolve` rather than passing a relative string.

**Two warnings before you copy this.** First, if your config already has a `build.rollupOptions` block, adding `build.rolldownOptions` makes Vite ignore the `rollupOptions` object entirely rather than merging it — see the precedence rule above.[^vite-src] Migrate everything across in the same edit. Second, the follow-up issue was **closed as not planned**, so this is not a bug awaiting a fix and the duplication may be permanent.[^alias-issue] Two other things the reporter tried — `configFile: false` alone, and the experimental native-plugin toggles — did not help.[^alias]

There is a related resolution change that produces similar-looking errors from a different cause. Vite used to sniff file contents when a package declared both `browser` and `module` fields, and would pick the ESM file for browsers. Vite 8 dropped that heuristic and always respects the order of `resolve.mainFields`.[^migration] If a package that previously resolved to its ESM build now resolves to a UMD one, that is why, and the documented remedies are a `resolve.alias` entry or a package manager patch.[^migration]

## Why does a `default` import from a CommonJS package now return an object?

Because Vite 8 made CommonJS `default` interop consistent, and consistency changes what some imports mean. Vite's troubleshooting page names the symptom exactly — "Default import unexpectedly returns an object" — and describes it as: "The default import returns the `module.exports` object for CJS modules, while you may expect it to return the `module.exports.default` value."[^troubleshooting]

That is worth stating carefully, because the intuitive guess is that the import becomes `undefined`. The documented failure runs the other way: you get the whole `module.exports` object where you expected one property off it. The symptom is usually `x.default is not a function`, or a component that renders as `[object Object]`, from a build that succeeded.

The rule the migration guide states is that the `default` import is the importee's `module.exports` value if any of these hold:[^migration]

- the importer is `.mjs` or `.mts`
- the importer's closest `package.json` has `"type": "module"`
- the importee's `module.exports.__esModule` is not `true`

Otherwise, the `default` import is `module.exports.default`.

Rolldown's own documentation, which the guide links for the full picture, lists a longer set of conditions than Vite's three — including two that apply only to dynamic imports, and one for the case where `module.exports` has no own `default` property at all.[^cjs] If Vite's three conditions do not explain what you are seeing, that page is where the remaining cases live.

Under Vite 7 the rules differed between dev and build, and in dev they additionally depended on whether the importer was included in dependency optimization.[^migration] That divergence is exactly what lets a bug reach production only after a build, and removing it is the point of the change.

This is not hypothetical. A Storybook Next.js integration filed it under the title "NextImage renders as object with Vite 8 due to Rolldown CJS interop change", surfaced through dependency pre-bundling in stories and browser tests; the reported symptom is React refusing the component with "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object."[^storybook-cjs] That error text is the tell — an *object* where a component was expected. The issue has since been closed with a linked fix, which is the normal shape of this: each affected package fixes its own unwrapping.

For a single import you can disambiguate at the call site rather than reaching for a global flag. Rolldown's documentation gives the test to use — check the `__esModule` marker rather than guessing:[^cjs]

```js
import raw from 'some-cjs-package'

const actual =
  typeof raw === 'object' && raw !== null && raw.__esModule ? raw.default : raw
```

The `__esModule` check is doing real work there, so resist shortening it to `raw.default ?? raw`. A plain CommonJS module that happens to export an object with its own `default` property, and does not set `__esModule`, is exactly the case Rolldown's heuristic hands you whole — and the shorter expression would silently unwrap it to that property instead. This paragraph is reasoning about the documented rule rather than something the docs spell out, so test it against the specific package rather than adopting it blind.

There is a deprecated global escape hatch if you need to ship today:

```js
// vite.config.js — temporary, deprecated
export default defineConfig({
  legacy: { inconsistentCjsInterop: true },
})
```

The migration guide is explicit that this is a stopgap and asks you to report affected packages upstream, with a link to the Rolldown documentation so the maintainer has the context.[^migration]

Two adjacent CommonJS changes travel with it. `require` calls for externalized modules are now preserved as `require` rather than converted to `import`; if you needed the old behaviour, Vite re-exports Rolldown's `esmExternalRequirePlugin` for it — which matters mainly for SSR and Node-targeted builds.[^migration] And `import.meta.url` is no longer polyfilled in UMD or IIFE output, where it is replaced with `undefined` by default.[^migration] That second one deserves a moment if you publish a library: `new URL('./worker.js', import.meta.url)` is the documented Vite pattern for workers and asset URLs, and in a UMD build it now becomes `new URL('./worker.js', undefined)` — a runtime error in your consumers, from a build that reported success. The guide's stated remedy is the `define` option combined with `build.rolldownOptions.output.intro`.[^migration]

## Why do my decorators fail to compile in Vite 8?

Start by checking which kind of decorators you have, because the answer is different and most people have the kind that still works.

**TypeScript's `experimentalDecorators` — the ones Angular, NestJS, TypeORM and MobX use — are supported, and you probably do not have to configure anything.** Oxc's transformer documentation is explicit: "Oxc transformer supports transforming legacy decorators. This is called experimental decorators in TypeScript. If you are using the `experimentalDecorators` option in the tsconfig, you can use the `decorator.legacy` option."[^oxc-ts] More usefully, Vite "respects some of the options in `tsconfig.json` and sets the corresponding Oxc Transformer options," and `experimentalDecorators` is on that list — so `"experimentalDecorators": true` in your tsconfig is the documented route, and Vite maps it across for you.[^features] If you do want to set it from the Vite config, that wins over tsconfig when both are present, and the path follows from Vite's `oxc` option extending Oxc's options: `oxc.decorator.legacy`.[^features][^shared-options]

**Native decorators — the TC39 stage 3 ones — are not lowered by Oxc yet**, and the Vite team is waiting on the specification to progress.[^migration] If that is what you have, the migration guide documents two workarounds: `@rolldown/plugin-babel` with `@babel/plugin-proposal-decorators`, and `@rollup/plugin-swc` with `@swc/core`. Both are filtered to files containing an `@`, using `rolldown: { filter: { code: '@' } }` and Vite's `withFilter` helper respectively.[^migration] The filters matter: reintroducing a JavaScript transform across every file gives back the build time Rolldown just saved.

```bash
npm install -D @rolldown/plugin-babel @babel/plugin-proposal-decorators
```

```js
// vite.config.ts
import { defineConfig } from 'vite'
import babel from '@rolldown/plugin-babel'

function decoratorPreset(options) {
  return {
    preset: () => ({
      plugins: [['@babel/plugin-proposal-decorators', options]],
    }),
    // Only run this transform if the file contains a decorator.
    rolldown: { filter: { code: '@' } },
  }
}

export default defineConfig({
  plugins: [babel({ presets: [decoratorPreset({ version: '2023-11' })] })],
})
```

Both of the guide's snippets target decorator version `2023-11`, so they are behaviourally comparable — but if you adapt either one, keep the version pinned rather than letting the plugin default decide, because the revisions differ in field-initialisation order.

On the metadata side, Vite 8 did add built-in support for TypeScript's `emitDecoratorMetadata`, which previously needed an external plugin — but the docs qualify it: "This option is only partially supported. Full support requires type inference by the TypeScript compiler, which is not supported."[^features] Oxc's own note is that it "will fallback to `Object` type if it cannot calculate the type of the decorator metadata."[^oxc-ts] For a dependency-injection container that keys on parameter types, an `Object` fallback is a runtime failure, not a rounding error.

One transform is genuinely unavailable: the migration guide lists "Transforming to ES5 and below with plugin-legacy is not supported."[^migration] The tracking issue it points at is still open, and its reporter has published a working Babel patch to `buildPolyfillChunk()` that produces an ES5 polyfills chunk — so "unsupported" here means "no supported path", not "no path at all".[^es5] If you ship to an ES5 matrix, that issue is where to look before concluding you cannot upgrade.

## Does Vite 8 still work with Yarn PnP?

Partly. The advice most commonly repeated in 2026 write-ups — switch `nodeLinker` to `node-modules` before upgrading — rests on reports that are now closed, while a newer report remains open downstream. This is the claim on this page most worth testing against your own project, so what follows is the trail rather than a verdict.

The reports that built the reputation are all in `rolldown-vite`, and all closed. "rolldown-vite does not play well with Yarn PnP" was opened on June 4, 2025 and describes builds consistently failing to resolve external dependencies under PnP.[^pnp215] "Native resolver + yarn pnp: [sass] Error: Can't find stylesheet to import." followed on August 25, 2025, and isolates the failure to the native-plugin resolver path.[^pnp392] "vite 8.0.0-beta.0 cannot resolve react dependency in Yarn PnP" was opened on December 5, 2025 and closed with a linked Rolldown pull request.[^pnp543] That repository was archived on March 19, 2026 and is read-only, so the staleness of all three is structural rather than incidental.[^pnp215]

Upstream, the tracking issue on Oxc's resolver is closed with a linked pull request, and its body states plainly: "Yarn PnP now works in v11.5.0 without any configuration."[^oxc-resolver] The page states no caveats. It also does not restate which project that version number belongs to — contextually oxc-resolver, but the actionable move is to check what is actually installed rather than to trust the number: `npm ls rolldown` will give you the Rolldown version in your tree, and Rolldown's release notes name the resolver version it bundles.

Cutting the other way, a Vitest issue opened on March 5, 2026 — "Regression in 4.1.0-beta.4 in Yarn PnP mode, tests hanging indefinitely on Vite 8" — was still open at the time of writing and labelled `upstream`.[^pnp-vitest] Read the mechanism before you weight it: the reporter's own diagnosis is that Vitest imports an optional dependency, `@opentelemetry/api`, which strict PnP refuses to resolve because it is not declared, and that installing it fixes the problem.[^pnp-vitest] That is a packaging regression surfaced by PnP's strictness, not evidence of a Rolldown resolver defect — which is exactly why it is worth naming precisely instead of citing it as proof.

So: do not rewrite your `.yarnrc.yml` on the strength of a 2025 issue in an archived repository, and do not assume PnP is clean either. Run the build and the test suite. `nodeLinker: node-modules` is a fallback you may not need.

## Why does my build now target newer browsers than before?

Because the default value of `build.target` moved. Vite 8's `'baseline-widely-available'` resolves to `['chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4']`, pinned to Baseline Widely Available as of 2026-01-01.[^build-options] On Vite 7 the same special value resolved to `['chrome107', 'edge107', 'firefox104', 'safari16']`.[^vite7-target]

Nothing errors. Your output simply stops being transpiled for the browsers between those two lines, and if your support matrix includes them you have shipped a regression without a warning. Set `build.target` explicitly if you have a real matrix to hit — the value must be a valid Oxc Transformer target, and Vite will warn at build time if your code contains features Oxc cannot safely transpile.[^build-options]

The second default change in the same area is `build.minify`, which is now `'oxc'` for client builds. Vite describes the Oxc Minifier as "30 ~ 90x faster than terser and only 0.5 ~ 2% worse compression."[^build-options] If you suspect the minifier is miscompiling your code, the migration guide points to both minifiers' published assumption documents so you can diff them rather than guess.[^migration]

## Do I still need esbuild installed?

Only if you opt back into it. The migration guide says esbuild "is no longer directly used by Vite and is now an optional dependency"; in the published package metadata for 8.2.2 that takes the form of `peerDependenciesMeta.esbuild.optional: true`, with the version range `^0.27.0 || ^0.28.0` declared under `peerDependencies`.[^migration][^npm]

You need to add it as a `devDependency` in three cases:[^migration][^build-options]

- `build.minify: 'esbuild'`, which is itself deprecated
- `build.cssMinify: 'esbuild'` — the most common reason today, per the CSS section above
- any plugin that calls `transformWithEsbuild`, which is deprecated in favour of `transformWithOxc`

If none of those apply, leave it out. Adding esbuild back "just in case" reintroduces a dependency the upgrade was designed to drop.

## How do I find out which of my plugins is breaking the build?

Bisect first, then read. The fastest route to the culprit is to halve your `plugins` array, rebuild, and repeat — a handful of builds beats reading a transitive plugin tree, and it works even when the failing plugin is one your framework installed rather than one you chose.

Three things sharpen that loop:

- **`vite build --debug`** for Vite's own debug output on the failing step.
- **`VITE_DEPRECATION_TRACE=1`** when the failure is a deprecated option rather than a crash. It turns the warning into a stack trace, which is the most direct way to tell *which plugin* set an option you never wrote.[^vite-src]
- **Peer ranges.** Check each plugin's declared `peerDependencies` on `vite`. A plugin that has not widened its range to `^8` is telling you something before you read a line of its source.

Once you have a suspect, four structural differences account for a great deal of plugin breakage:[^migration]

1. **Missing hooks.** `shouldTransformCachedModule`, `resolveImportMeta`, `renderDynamicImport`, and `resolveFileUrl` are listed under "Missing support by Rolldown" and are no longer supported. The guide does not say what an affected plugin does at runtime, so treat a plugin that uses one as suspect rather than waiting for a diagnostic message.
2. **Sequential hooks.** "All parallel hooks in Rollup work as sequential hooks" under Rolldown.
3. **The `bundle` object.** Assigning to `bundle[foo]` is no longer supported — use `this.emitFile()`. The reference is not shared across hooks, and `structuredClone(bundle)` now throws `DataCloneError`; clone `{ ...bundle }` instead.
4. **Module types.** Rolldown auto-assigns a module type from the resolved id's extension. A plugin converting non-JS content to JavaScript in `load` or `transform` may need to return `moduleType: 'js'` explicitly.

Vite launched `registry.vite.dev` alongside Vite 8, a searchable directory of Vite, Rolldown and Rollup plugins built from daily npm data, which is the right place to check a plugin's current state rather than any static list.[^announce]

If you call Vite's JavaScript API directly, one more change matters: `build()` now throws a `BundleError`, typed as `Error & { errors?: RolldownError[] }`, which wraps individual errors in an array. Existing `catch` blocks that read `e.message` will report something far less useful than `e.errors`.[^migration]

## Why did a patch release of Vite 8 break a build that worked yesterday?

Because Rolldown is still moving underneath Vite's patch releases, and two documented regressions landed that way. This is its own bucket because the diagnostic is different: nothing in your config changed, so the useful first step is to pin backwards and confirm, not to edit anything.

An issue opened April 24, 2026 reports Astro builds failing after updating from **8.0.8 to 8.0.10** with `Missing field 'tsconfigPaths' on BindingViteResolvePluginConfig.resolveOptions`, thrown from `@tailwindcss/vite`; the reporter attributes it to a Rolldown release-candidate bump, and a second variant of the same symptom names a different missing field.[^resolver22322] It was closed as not planned.

An issue opened July 1, 2026 reports a build failing from **8.1.0 onward** with `[MISSING_EXPORT] "placements" is not exported by "node_modules/@popperjs/core/lib/index.js"`, on a project that builds cleanly on 8.0.16.[^resolver22835] It was closed as a duplicate. Two details from it are worth carrying: the reporter's stated workaround is literally `npm install vite@=8.0.16`, and the failure is build-only — "There is no issue with `npm run dev`", which is the trap, because a green dev server is not evidence.[^resolver22835]

The practical response is to treat the Vite patch version as a variable in your bisect. If the build broke without a config change, install the previous patch, confirm, and then read the changelog between the two rather than searching your own source.

## Why does it build locally but fail in CI?

Because two Vite 8 failure modes are properties of the environment rather than of your code, and neither is about your Node version.

**The native binary.** Rolldown ships its platform-specific binaries as npm `optionalDependencies` — fifteen of them in the current release, one per target.[^rolldown-npm] That is the same packaging pattern that produced Rollup's long-running install failures, and Rolldown inherits it. The outer symptom is `Error: Cannot find native binding`, wrapping a `Cannot find module '../rolldown-binding.<platform>.node'`.[^binding] Rolldown's troubleshooting page addresses that inner error under the heading `Error: "Cannot find module '@rolldown/binding-...'"` and names the cause: "It is usually caused by a known npm bug with optional dependencies (npm/cli#4828); if you installed with npm, removing `node_modules` and `package-lock.json` and reinstalling fixes it."[^rolldown-ts] The same page documents a second cause worth knowing if you develop across Windows and WSL: a config file in a symlinked directory can resolve `rolldown` to a `node_modules` installed for a different platform, for which the remedies are keeping the config outside the symlinked directory or setting `NODE_OPTIONS=--preserve-symlinks` — with the caveat, stated on that page, that the flag is "not compatible with pnpm, whose `node_modules` layout relies on symlinks."[^rolldown-ts]

A community report adds one more trigger that is easy to miss because it looks unrelated. A commenter found that changing their `package.json` `engines` field from `"node": ">22"` to `">=22.18.0"` "caused the binary to download", and flagged the package manager's `minimumReleaseAge` setting as another possible culprit.[^binding] The semver reading is mine rather than theirs, and it is at least consistent: `>22` resolves to `>=23.0.0`, which excludes the 22.12–22.x range Rolldown declares support for.[^rolldown-npm] If your CI image is Alpine or musl-based, also check that you are getting the musl binding rather than the gnu one.

**Memory.** This is the one the install-size figures do not prepare you for. An open, assigned Rolldown issue on the "Vite 8 migration blockers" board reports roughly a sevenfold increase in physical memory footprint in dev mode — about 880 MB on Vite 7.3.3 against about 6.7 GB on Vite 8.0.11 — in one large project.[^mem9330] Read the reporter's own caveats with it: they have not separately bisected `@vitejs/plugin-react` 4 to 5, so part of the regression may live there; there is no minimal reproduction yet; and the regression is present from the first Vite 8 beta rather than appearing in a specific patch, with the drift across stable releases "dwarfed by the v7→v8 step."[^mem9330] It is one project's measurement, not a benchmark. But on a constrained CI runner an out-of-memory kill often surfaces as a non-zero exit with no useful message, which reads exactly like "the build fails after upgrading."

Neither of these is covered by Vite's or Rolldown's documentation beyond the native-binding page, so treat memory in particular as something to measure on your own runner rather than something with a documented answer.

## How do I migrate gradually, or roll back?

Use the official two-step path forward. The `rolldown-vite` package implements Vite 7 with Rolldown and none of the other Vite 8 changes, which isolates bundler problems from everything else, and Vite's recommendation for larger projects is to switch to it on Vite 7 first, then upgrade.[^migration][^announce] Leaving it behind is the one-line change the guide shows — replacing a `"vite": "npm:rolldown-vite@7.2.2"` entry with `"vite": "^8.0.0"`.[^migration]

Rolling back to Vite 7 is more than a version bump, because the upgrade pulled coupled majors with it. Clear the lockfile and tree first, so the reinstall resolves cleanly rather than against stale entries for `rolldown`, `lightningcss` and the native bindings:

```bash
rm -rf node_modules package-lock.json
# edit package.json: vite ^7, and revert your framework plugin's major
npm install
```

Two things to check while you are there. Your framework plugin needs a version whose peer range accepts Vite 7 — `@vitejs/plugin-react@5.2.0` declares `^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0`, which makes that specific release a safe stopping point in both directions; earlier 5.x releases stop at Vite 7, and the v6 line requires Vite 8.[^plugin-react-npm] And note that `node_modules/.vite` is Vite's dependency-optimization cache and lives inside `node_modules`, so deleting the latter already clears it — but a cache written by Rolldown is not something Vite 7 should read, so do not skip that step. The Vite 7 documentation stays live at `v7.vite.dev`, which the Vite 8 docs link directly.[^migration]

Vitest is not the constraint people assume it is: `vitest@4.1.11` declares its Vite peer range as `^6.0.0 || ^7.0.0 || ^8.0.0`, so that line spans three Vite majors and a rollback does not force a Vitest downgrade.[^vitest-npm] Check your own installed version rather than assuming, since the range has moved across Vitest releases.

One caution on sequencing that does apply to some stacks. On a plain React or Vue app you can upgrade Vite first and the plugin afterwards, which makes a bisect meaningful.[^announce] On a meta-framework — Storybook, Nuxt, SvelteKit, Astro, React Router — the peer range often decides the order for you, and your package manager will either refuse the install or produce a broken tree. Check the framework's supported Vite range before you plan the sequence rather than after. If you do move `@vitejs/plugin-react` to v6, it uses Oxc for the React Refresh transform and drops Babel as a dependency, with a `reactCompilerPreset` helper for projects that need the React Compiler — a different setup from the Babel-based one v5 shipped, and worth reading about first if you have ever debugged [React Compiler not memoizing a component](https://nerdleveltech.com/react-compiler-not-memoizing-component).[^announce]

## Is the build actually faster, and what does it cost?

Faster, on the available evidence, and the evidence is entirely vendor-supplied. Vite reports Rolldown as 10-30x faster than Rollup in benchmarks, and names four companies with measured production build times: Linear from 46s to 6s, Ramp with a 57% reduction, Mercedes-Benz.io up to 38%, and Beehiiv 64%.[^announce] Those were reported by the adopters during the preview and beta phases, on their own codebases. I found no independent published benchmark of Vite 8 against Vite 7 as of September 2026, so treat the range as directional.

The disclosed cost is about **15 MB** of additional install size versus Vite 7: roughly 10 MB because Lightning CSS moved from an optional peer dependency to a normal one, and roughly 5 MB for the Rolldown binary, which is larger than esbuild plus Rollup because it favours speed over binary size.[^announce] Vite says it will keep working to reduce this.[^announce] The disk figure is the disclosed cost; the memory figures in the CI section above are the undisclosed one.

Node requirements did not change: Vite 8 needs Node 20.19+ or 22.12+, the same as Vite 7, and the published `engines` field for 8.2.2 confirms `^20.19.0 || >=22.12.0`.[^announce][^npm] That makes the Node version a non-issue — but as the CI section covers, it is not the only thing about your CI environment that matters.

## FAQ

**Was `build.rollupOptions` removed in Vite 8?**
No. It is documented as an alias of `build.rolldownOptions` and marked deprecated. It still works, and renaming it will not fix a failing build.[^build-options]

**What happens if I set both `build.rollupOptions` and `build.rolldownOptions`?**
`rolldownOptions` wins and `rollupOptions` is ignored entirely, not merged. The docs do not state this; Vite's source does, in a comment on the line that implements it.[^vite-src]

**What was actually removed in Vite 8?**
`build.rollupOptions.watch.chokidar`, the object form of `output.manualChunks`, and URL arguments to `import.meta.hot.accept`. Separately, the `system` and `amd` formats and four Rollup hooks are unsupported by Rolldown; `esbuild.supported` and esbuild's property-mangling options are unsupported by Oxc; and `build.commonjsOptions` still parses but is now a no-op.[^migration]

**How do I replace `manualChunks: { vendor: [...] }` in Vite 8?**
With `build.rolldownOptions.output.codeSplitting`, using `groups` entries whose optional `test` takes a string, a regular expression, or a predicate over the module id — not a list of package names. Expect a forced `runtime.js` chunk and recursive dependency capture.[^rolldown-cs]

**My CSS broke after upgrading. What changed?**
`build.cssMinify` now defaults to `'lightningcss'`. It can reject modern syntax such as `@scope`, and it can drop an unprefixed `backdrop-filter` that sits before its `-webkit-` counterpart, with no error. Try a Lightning CSS version override or `build.cssTarget` before reverting the whole pipeline with `build.cssMinify: 'esbuild'`.[^build-options][^css21911][^css22649][^css21954]

**What does `The "TopLevelAwait" is not supported with the "iife" output format` mean?**
Something in the bundle uses top-level `await` and the output format cannot express it. Rolldown only emits top-level `await` in `esm` format, so switching to UMD or CJS will not help. The usual cause is workers, since `worker.format` defaults to `'iife'`; try `worker: { format: 'es' }`.[^rolldown-tla][^worker-options][^tla]

**Why does `resolve.alias` not work in my programmatic `build()` call?**
In the one verified case — a library-mode `build()` invoked from a Cypress preprocessor — the alias never reaches Rolldown; the same function works standalone. Declare it under `build.rolldownOptions.resolve.alias` too, but migrate any existing `build.rollupOptions` in the same edit, because setting both discards the latter. The follow-up issue was closed as not planned.[^alias][^alias-issue][^vite-src]

**Why does my `default` import from a CommonJS package return an object?**
Vite 8 unified the CJS `default` interop rules across dev and build, so in an ESM context you now get `module.exports` rather than `module.exports.default`. For a single import, disambiguate with Rolldown's `__esModule` test rather than the deprecated global `legacy.inconsistentCjsInterop` flag.[^troubleshooting][^migration][^cjs]

**Do decorators work in Vite 8?**
TypeScript's `experimentalDecorators` do — Oxc supports them through its `decorator.legacy` option. Native TC39 decorators are not lowered yet, and the migration guide documents Babel and SWC workarounds. `emitDecoratorMetadata` is supported but, per the docs, "only partially".[^oxc-ts][^migration][^features]

**Does Vite 8 support Yarn PnP?**
The three tracked reports in `rolldown-vite` are closed and that repository was archived on March 19, 2026, and Oxc's resolver issue states PnP "now works in v11.5.0 without any configuration". One newer Vitest issue was still open at the time of writing, though its stated cause is an undeclared optional dependency rather than a resolver defect. Test your own build and test suite.[^pnp215][^pnp392][^pnp543][^oxc-resolver][^pnp-vitest]

**Why does my bundle target newer browsers now?**
`'baseline-widely-available'` moved from `['chrome107', 'edge107', 'firefox104', 'safari16']` on Vite 7 to `['chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4']` on Vite 8. Set `build.target` explicitly if you have a real support matrix.[^vite7-target][^build-options]

**Do I still need esbuild installed?**
Only for `build.minify: 'esbuild'`, `build.cssMinify: 'esbuild'`, or a plugin calling `transformWithEsbuild`. It is an optional dependency in Vite 8, declared with the peer range `^0.27.0 || ^0.28.0`.[^migration][^npm]

**Why does it build locally but fail in CI?**
Usually one of two things: Rolldown's platform-specific native binary failing to install, which Rolldown's troubleshooting page attributes to a known npm optional-dependency bug; or memory, where one open report measures roughly a sevenfold increase in dev-mode footprint versus Vite 7.[^rolldown-ts][^rolldown-npm][^mem9330]

**A patch release broke my build. Is that expected?**
It has happened at least twice in documented cases — a resolver-plugin config regression between 8.0.8 and 8.0.10, and a missing-export failure from 8.1.0 that does not reproduce in the dev server. Pin backwards to confirm before editing your config.[^resolver22322][^resolver22835]

**Can I upgrade gradually?**
Yes. Switch to the `rolldown-vite` package on Vite 7 first to isolate bundler issues, then move to Vite 8.[^migration][^announce]

**How much bigger is Vite 8 to install?**
Roughly 15 MB larger than Vite 7 — about 10 MB from Lightning CSS becoming a normal dependency and about 5 MB from the Rolldown binary.[^announce]

## Bottom line

Vite 8's compatibility layer does more work than its reputation suggests, and a lot of the migration advice circulating in 2026 spends its opening paragraphs on a rename that is not required to get a build green. The failures that actually stop builds are narrower and more specific: a stricter CSS minifier, an ESM-only rule for top-level `await`, unified CommonJS semantics, native decorators with no lowering step, a short list of removed options — and, for a good number of teams, an environment problem in CI that has nothing to do with any of them.

Four steps, in order. Run the build unchanged and read the error against the seven buckets above. If it fails only in CI, go to the native-binary and memory section before you touch config. If the failure is in CSS, try a Lightning CSS override or `build.cssTarget` before reverting the whole pipeline. And if it is anything else, register a `configResolved` plugin and print what the compatibility layer produced before you change a single line.

Then leave the deprecation cleanup for a separate commit — and when you do it, move each `rollupOptions` object across whole, since a half-migrated config silently loses the half you left behind. If you are also modernising the rest of your toolchain, the same "Rust rewrite, same API surface" pattern is worth reading about in [TypeScript 7's native compiler](https://nerdleveltech.com/typescript-7-native-compiler-tsgo), and if this upgrade is bundled with a CSS overhaul, [Tailwind v4 with Vite and CSS-first `@theme` config](https://nerdleveltech.com/tailwind-v4-vite-css-first-theme-config-tutorial) covers the plugin side of that pairing.

## References

[^announce]: "Vite 8.0 is out!", Vite blog, March 12, 2026 — https://vite.dev/blog/announcing-vite8 — release date, Rolldown rationale, 10-30x claim, named adopter build times, install size breakdown, Node requirements, `@vitejs/plugin-react` v6, registry.vite.dev, gradual migration recommendation.

[^migration]: "Migration from v7", Vite documentation — https://vite.dev/guide/migration — deprecated vs removed vs unsupported options, auto-conversion tables, CJS interop rules, decorator workarounds, browser target change, plugin and JS API changes. Retrieved September 3, 2026 against the Vite 8.2.2 docs.

[^build-options]: "Build Options", Vite configuration reference — https://vite.dev/config/build-options — `build.rollupOptions` documented as a deprecated alias, `build.target` resolved values, `build.cssMinify` and `build.minify` defaults, `build.cssTarget` precedence, esbuild install requirement.

[^worker-options]: "Worker Options", Vite configuration reference — https://vite.dev/config/worker-options — `worker.format` type `'es' | 'iife'` with default `'iife'`; `worker.rollupOptions` documented as a deprecated alias.

[^shared-options]: "Shared Options", Vite configuration reference — https://vite.dev/config/shared-options — the `oxc` option, documented as extending Oxc Transformer's options.

[^features]: "Features", Vite documentation — https://vite.dev/guide/features — `emitDecoratorMetadata` support is "only partially supported. Full support requires type inference by the TypeScript compiler, which is not supported."

[^troubleshooting]: "Troubleshooting — Default import unexpectedly returns an object", Vite documentation — https://vite.dev/guide/troubleshooting — "The default import returns the `module.exports` object for CJS modules, while you may expect it to return the `module.exports.default` value."

[^npm]: `vite@8.2.2` package metadata, npm registry — https://registry.npmjs.org/vite/latest — current version, `engines` field, `rolldown` and `lightningcss` dependencies, and `esbuild` listed under `peerDependencies` (`^0.27.0 || ^0.28.0`) with `peerDependenciesMeta.esbuild.optional: true`.

[^vite-src]: Vite source, `packages/vite/src/node/utils.ts` — https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts — the comment and implementation "if both rollupOptions and rolldownOptions are present, ignore rollupOptions and use rolldownOptions"; the deprecation warning gated to `optimizeDeps` and `ssr.optimizeDeps`; and the `VITE_DEPRECATION_TRACE` switch from `console.warn` to `console.trace`. The plugin-level "will be ignored" warning is in `packages/vite/src/node/config.ts`.

[^rolldown-cs]: "OutputOptions.codeSplitting" and "Manual Code Splitting", Rolldown documentation — https://rolldown.rs/reference/OutputOptions.codeSplitting and https://rolldown.rs/in-depth/manual-code-splitting — the `groups` / `name` / `test` shape, the forced `runtime.js` chunk, and recursive dependency capture.

[^rolldown-tla]: "TLA in Rolldown", Rolldown documentation — https://rolldown.rs/in-depth/tla-in-rolldown — "If your input contains TLA, it could only be bundled and emitted with `esm` format."

[^rolldown-ts]: "Troubleshooting", Rolldown documentation — https://rolldown.rs/guide/troubleshooting — the `Cannot find module '@rolldown/binding-...'` section, the npm optional-dependency bug (npm/cli#4828), and the symlinked-config / `--preserve-symlinks` case with its pnpm caveat.

[^rolldown-npm]: `rolldown` package metadata, npm registry — https://registry.npmjs.org/rolldown/latest — fifteen platform-specific bindings declared under `optionalDependencies`, and the `engines` range `^20.19.0 || >=22.12.0`.

[^oxc-ts]: "TypeScript", Oxc Transformer documentation — https://oxc.rs/docs/guide/usage/transformer/typescript — "Oxc transformer supports transforming legacy decorators. This is called experimental decorators in TypeScript", the `decorator.legacy` and `decorator.emitDecoratorMetadata` options, and the `Object` type fallback for decorator metadata.

[^cjs]: "Bundling CJS", Rolldown documentation — https://rolldown.rs/in-depth/bundling-cjs — the full condition list for ambiguous `default` imports from CJS modules, which is longer than the three conditions Vite's migration guide states, and the `__esModule` test used to disambiguate a single import.

[^storybook-cjs]: "[Bug] NextImage renders as object with Vite 8 due to Rolldown CJS interop change", storybookjs/vite-plugin-storybook-nextjs issue #114, opened March 24, 2026 — https://github.com/storybookjs/vite-plugin-storybook-nextjs/issues/114 — a real-world instance of the symptom, reported via dependency pre-bundling in stories and browser tests, with React's "Element type is invalid … but got: object" as the surfaced error. Closed, with a linked pull request.

[^vitest-npm]: `vitest@4.1.11` package metadata, npm registry — https://registry.npmjs.org/vitest/4.1.11 — the `vite` peer dependency range `^6.0.0 || ^7.0.0 || ^8.0.0`. Pinned deliberately: the `/latest` endpoint moves, and later Vitest releases narrow this range.

[^plugin-react-npm]: `@vitejs/plugin-react@5.2.0` package metadata, npm registry — https://registry.npmjs.org/@vitejs/plugin-react/5.2.0 — `vite` peer range `^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0`. Pinned deliberately: earlier 5.x releases omit Vite 8, and the v6 line requires it.

[^css21911]: "Default CSS minifier in Vite@8 blocks some progressive modern CSS features", vitejs/vite issue #21911, opened March 17, 2026 — https://github.com/vitejs/vite/issues/21911 — `@scope` rejected during minification; open and labelled `bug: upstream` at the time of writing.

[^css22649]: "Vite 8 default `cssMinify: 'lightningcss'` drops unprefixed `backdrop-filter`, breaking glass/blur effects (regression vs Vite 7)", vitejs/vite issue #22649, opened June 9, 2026 — https://github.com/vitejs/vite/issues/22649 — reported on Vite 8.0.16 with lightningcss 1.32.0; closed as a duplicate of #21954.

[^css21954]: "`backdrop-filter` before `-webkit-backdrop-filter` is dropped with `cssMinify: 'lightningcss'`", vitejs/vite issue #21954, opened November 20, 2025 — https://github.com/vitejs/vite/issues/21954 — open and labelled `bug: upstream` at the time of writing; the issue #22649 was closed against.

[^tla]: "Build fails with top-level await in @novnc/novnc after 8.0.6+ rolldown switch", vitejs/vite issue #22314, opened April 23, 2026 — https://github.com/vitejs/vite/issues/22314 — 8.0.10 fails where 8.0.5 succeeded; closed, with no workaround stated on the page.

[^alias]: "[Vite 8] `build()` library mode: `resolve.alias` does not work unless included in `rolldownOptions`", vitejs/vite discussion #22377, opened May 1, 2026 — https://github.com/vitejs/vite/discussions/22377 — reported on Vite 8.0.10, with the error `Rolldown failed to resolve import "@/model/Alert.model"`. The marked answer is the author's own note that he opened issue #22410.

[^alias-issue]: "[Vite 8] `build()` library mode: `resolve.alias` does not work unless included in `build.rolldownOptions`", vitejs/vite issue #22410, opened May 8, 2026 — https://github.com/vitejs/vite/issues/22410 — reported on Vite 8.0.11; **closed as not planned**. Contains the reproduction showing the standalone build succeeding and only the Cypress preprocessor path failing, and the error `Rolldown failed to resolve import "@/foo.js"`.

[^resolver22322]: "Vite 8.0.10 public resolver APIs produce incomplete rolldown plugin configs", vitejs/vite issue #22322, opened April 24, 2026 — https://github.com/vitejs/vite/issues/22322 — `Missing field \`tsconfigPaths\` on BindingViteResolvePluginConfig.resolveOptions` on Astro builds via `@tailwindcss/vite`; 8.0.8 working, 8.0.10 broken; closed as not planned.

[^resolver22835]: "vite resolver broken since `8.1.0`", vitejs/vite issue #22835, opened July 1, 2026 — https://github.com/vitejs/vite/issues/22835 — `[MISSING_EXPORT] "placements" is not exported by "node_modules/@popperjs/core/lib/index.js"` on 8.1.2, working on 8.0.16; the reporter notes "There is no issue with `npm run dev`". Closed as a duplicate of #22779.

[^binding]: "[Bug]: rolldown-binding.linux-x64-gnu.node missing (pnpm install)", rolldown/rolldown discussion #9098, opened April 10, 2026 — https://github.com/rolldown/rolldown/discussions/9098 — `Error: Cannot find native binding` with `librt.so.1: cannot open shared object file`; unanswered at the time of writing. The `engines: ">22"` trigger is a commenter's finding, not a maintainer statement.

[^mem9330]: "[Bug]: rolldown 1.0.0-rc.18 in vite 8 dev mode shows ~7× higher physical memory footprint vs vite 7 (rollup + esbuild)", rolldown/rolldown issue #9330, opened May 9, 2026 — https://github.com/rolldown/rolldown/issues/9330 — open, assigned, labelled `scope: perf` and tracked on the "Vite 8 migration blockers" board; ~880 MB on Vite 7.3.3 against ~6.7 GB on Vite 8.0.11 in one project. The reporter states they have not bisected `@vitejs/plugin-react` 4→5 separately and have no minimal reproduction.

[^es5]: "legacy: support lowering to ES5", vitejs/vite issue #21951, opened October 17, 2025 — https://github.com/vitejs/vite/issues/21951 — the tracking issue the migration guide points at for `@vitejs/plugin-legacy`; open, and containing a reporter-published Babel patch to `buildPolyfillChunk()`.

[^pnp215]: "rolldown-vite does not play well with Yarn PnP", vitejs/rolldown-vite issue #215, opened June 4, 2025 — https://github.com/vitejs/rolldown-vite/issues/215 — closed; the repository banner records that it was archived on March 19, 2026 and is read-only.

[^pnp392]: "Native resolver + yarn pnp: [sass] Error: Can't find stylesheet to import.", vitejs/rolldown-vite issue #392, opened August 25, 2025 — https://github.com/vitejs/rolldown-vite/issues/392 — closed, with a linked rolldown/rolldown pull request; reproduces only with native plugins enabled.

[^pnp543]: "vite 8.0.0-beta.0 cannot resolve react dependency in Yarn PnP", vitejs/rolldown-vite issue #543, opened December 5, 2025 — https://github.com/vitejs/rolldown-vite/issues/543 — closed, with a linked rolldown/rolldown pull request.

[^pnp-vitest]: "Regression in 4.1.0-beta.4 in Yarn PnP mode, tests hanging indefinitely on Vite 8", vitest-dev/vitest issue #9799, opened March 5, 2026 — https://github.com/vitest-dev/vitest/issues/9799 — open and labelled `upstream` at the time of writing; the reporter identifies an undeclared optional dependency, `@opentelemetry/api`, as the thing strict PnP refuses to resolve, and notes that installing it fixes the problem.

[^oxc-resolver]: "Yarn PnP", oxc-project/oxc-resolver issue #53, opened January 12, 2024 — https://github.com/oxc-project/oxc-resolver/issues/53 — closed with a linked pull request; body states "Yarn PnP now works in v11.5.0 without any configuration."

[^vite7-target]: "Build Options", Vite 7 configuration reference — https://v7.vite.dev/config/build-options — Vite 7's `'baseline-widely-available'` resolves to `['chrome107', 'edge107', 'firefox104', 'safari16']`.
