
Upgrade Vite 7 to Vite 8 Without a Surprise
Use a safe Vite 7 to Vite 8 migration path: check Node support, isolate Rolldown, update optimizer config, and test HMR and production output.
The safe Vite 8 upgrade path
Vite 8 replaces Vite’s previous Rollup and esbuild pipeline with Rolldown and Oxc. Treat it as a bundler migration, not a routine package bump. The safest order is: make your Vite 7 build clean, test the same project with rolldown-vite, then move to Vite 8 and compare development and production behavior.
The official Vite 8 migration guide recommends rolldown-vite as an intermediate step. That gives you a useful answer when something breaks: did Rolldown cause it, or did another Vite 8 change cause it?
What to check before changing package.json
Start with the runtime. Vite 8 requires Node.js 20.19+ or 22.12+. Check the version in local development, CI, container images, and any deployment build environment. An upgrade that succeeds on a developer laptop but runs an older Node version in CI is not a Vite regression.
Then look for custom bundler behavior. Search vite.config.*, package.json, and plugin code for optimizeDeps.esbuildOptions, direct Rollup bundle mutation, import.meta.hot.accept calls that pass URLs, and plugins that depend on esbuild-specific transforms. These are the places where the Vite 8 migration guide calls out changed or deprecated behavior.
// First, make the Vite 7 project explicit.
{
"engines": {
"node": ">=20.19.0 || >=22.12.0"
},
"devDependencies": {
"vite": "^7.0.0"
}
}This is a policy example, not a command you should paste over an existing package.json. Preserve the Node range your project and deployment platform actually support.
Use rolldown-vite as a diagnostic step
Vite’s Vite 8 announcement describes the recommended two-step route for larger or more complex projects: first use the rolldown-vite package on the Vite 7 line, then upgrade to Vite 8. The intermediate package isolates the bundler change from the rest of the major-version changes.
Do this on a small branch with a clean lockfile diff. Run the same scripts CI runs. In addition to a build, open the routes that use dynamic imports, CSS Modules, worker code, SSR, and any package linked from a workspace. A green homepage proves very little about a bundler migration.
// package.json, temporary diagnostic step
{
"devDependencies": {
"vite": "npm:rolldown-vite@7.2.2"
}
}Use the version recommended in the migration guide for the Vite 7 line you are upgrading from. Do not leave this alias in place after the Vite 8 move. It is a test stage, not a permanent fork of your toolchain.
Move dependency-optimizer configuration deliberately
Vite 8 uses Rolldown for dependency optimization. Vite still converts optimizeDeps.esbuildOptions for compatibility, but the option is deprecated. Migrate only the options you actually use, then check that the development server still resolves and prebundles the same dependencies.
// Before: Vite 7 style
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
minify: false,
treeShaking: false,
},
},
})
// After: Vite 8 style
export default defineConfig({
optimizeDeps: {
rolldownOptions: {
output: { minify: false },
treeshake: false,
},
},
})The values above illustrate the documented mapping. They are not a recommendation to disable minification or tree shaking. Keep a setting only when you can explain the failure it prevents, and record that reason near the configuration.
Do not call a URL in hot accept
Vite 8 no longer supports passing a URL to import.meta.hot.accept. Pass a module ID instead. This tends to affect hand-written HMR boundaries or older plugins more than ordinary application code.
// Before
if (import.meta.hot) {
import.meta.hot.accept("/src/widget.ts", () => {})
}
// After
if (import.meta.hot) {
import.meta.hot.accept("./widget.ts", () => {})
}The important part is not the exact string from this small example. It is to test the actual HMR boundary after the upgrade. Save the imported module, then save the parent module, and confirm the expected state reset or preservation behavior in the browser.
Compare the production output, not only dev
Vite 8 changes the default browser target to a newer Baseline Widely Available set. That can be the right choice, but it may remove support for browsers your product still promises to serve. If you support an older browser, set build.target deliberately and keep that requirement in your browser-support policy.
Make a before-and-after table for a representative build: build command exit status, generated routes, manifest entries if you use them, asset URLs, dynamic imports, and any SSR output. Use an end-to-end smoke test for login or checkout flows when they are in scope. A lockfile diff and a screenshot alone do not tell you whether a chunk-loading path changed.
When to stop and investigate
The Vite issue tracker has active Vite 8 reports around CSS dynamic imports and CSS module loading from HTML. These reports are not proof that every project will fail, but they are a reason to include CSS and dynamic-import paths in your upgrade test matrix.
Stop the migration when dev and build disagree, a plugin relies on a Rollup-only assumption, or a browser target changes without an explicit product decision. Capture a minimal reproduction before changing several config flags at once. A small reproduction lets you decide whether the problem belongs in your app, a plugin, or Vite itself.
A practical release checklist
1. Pin a Node version supported by Vite 8 in local development and CI.
2. Run the existing Vite 7 build and tests from a clean install.
3. Test rolldown-vite on the Vite 7 line if the project has custom plugins, SSR, workspaces, or unusual build output.
4. Upgrade to Vite 8, migrate only documented configuration you use, and inspect the lockfile.
5. Test development HMR, dynamic imports, CSS Modules, production build output, and a real browser flow.
6. Keep the rollback as the prior lockfile and package version until the deployment has completed its normal traffic and scheduled work.
Vite 8 can be a straightforward upgrade. The reliable way to keep it that way is to make the Rolldown change visible, test the edges your project actually has, and treat any configuration change as a behavior change.