DEV Community

Cover image for Migrating 8 SvelteKit Sites to Vite 8 in a day: What We Learned
Rick Cogley
Rick Cogley

Posted on • Originally published at cogley.jp

Migrating 8 SvelteKit Sites to Vite 8 in a day: What We Learned

Vite 8 dropped on March 12, 2026. Two days later, we'd migrated 8 SvelteKit projects — a mix of monorepos and standalone sites, all deployed to Cloudflare Workers. The migration was mostly mechanical, and the builds got faster. Let me share a couple lessons learned.

What Vite 8 Changes

Vite 8 replaces a kind of "split-brain" bundler architecture. Vite 7 used esbuild for development transforms and dependency optimization, then switched to Rollup for production builds. Two different tools doing overlapping jobs, with subtly different behavior in each environment.

Vite 8 unifies to use only Rolldown, a Rust-based bundler. Oxc handles JavaScript transforms and minification (replacing esbuild's role), and Lightning CSS takes over CSS minification. One bundler means consistent dev and production.

So is Vite 8 any good? Yes! Looking at our Cloudflare UI for each worker, overall build times dropped about 15s in most projects, with a couple dropping ~45-60sec. That's mostly 10-15%, with a couple dropping 40-50%. One caveat: Vite 8 gains are in the bundling phase of the build, but if your build time is dominated by Vite plugins (Sentry, SvelteKit preprocessing, etc.), the improvement will be smaller. This is admittedly unscientific, since there are other factors like Cloudflare being sluggish, but better build times are better for everybody.

The Migration Itself

For SvelteKit projects that don't customize Vite's internals, the migration was four commands:

pnpm update vite@^8.0.0 --recursive
pnpm update @sveltejs/kit@latest --recursive
pnpm update @sveltejs/vite-plugin-svelte@latest --recursive
pnpm update @sveltejs/adapter-cloudflare@latest --recursive
Enter fullscreen mode Exit fullscreen mode

Note that @sveltejs/vite-plugin-svelte jumps a major version (6 → 7) for Vite 8 support. This is the critical dependency that must cross a major boundary, and it's easy to miss. The pnpm update command above handles it automatically, but if you manually edit package.json, you need to change the range from "^6" to "^7" — a ^6 range won't resolve to 7.x, and your build will fail with a peer dependency mismatch against Vite 8. The adapter-cloudflare update is a minor patch (e.g., 7.2.5 → 7.2.8).

Also make sure vitest is on 4.x — our projects worked fine on 4.0.18. If you're still on vitest 3.x, be aware you're facing two major version upgrades at once (vitest 3 → 4 and Vite 7 → 8). Vitest 3.x does not support Vite 8's Rolldown bundler1. We'd recommend upgrading vitest to 4.x first on Vite 7, verifying your test suite still passes, then doing the Vite 8 bump as a separate step.

SvelteKit supports Vite 8 as of @sveltejs/kit@2.53.x2, though the latest minor (2.55.0 at time of writing) has additional fixes worth picking up. After updating, verify every workspace package resolves to Vite 8:

pnpm ls vite --recursive --depth 0
Enter fullscreen mode Exit fullscreen mode

If any package still pins Vite 7, update its package.json directly. Turborepo won't save you from a stale lockfile entry.

Config Renames

Vite 8 deprecates a handful of config keys. The old names still work through a compatibility layer, but migrating now avoids warnings and future breakage:

Deprecated Replacement
build.rollupOptions build.rolldownOptions
worker.rollupOptions worker.rolldownOptions
optimizeDeps.esbuildOptions optimizeDeps.rolldownOptions
esbuild (top-level) oxc

One rename that looks straightforward but isn't: esbuildoxc. If you use esbuild: { drop: ['console', 'debugger'] } to strip debug statements in production builds, the replacement lives in a different location: build.rolldownOptions.output.minify.compress.drop*3. The deprecated esbuild key still works via the compat layer, but migrating to the Rolldown-native option is cleaner. One subtle gotcha: if you pass an empty array [] for drop in non-production mode, change it to undefined — the compat layer doesn't handle empty arrays the same way Vite 7's esbuild did.

Six of our eight projects had no custom Vite config at all, relying on SvelteKit's defaults. For those, the migration was literally just version bumps — zero code or config changes. If you use Tailwind CSS v4 with @tailwindcss/vite, that works with Vite 8 without changes too. Two projects had rollupOptions for manual chunk splitting, which needed attention.

The manualChunks Removal

Vite 8 removes the object form of output.manualChunks entirely3, so you might need to handle this. The function form is deprecated. If you were splitting vendor chunks explicitly:

// This no longer works in Vite 8
build: {
  rollupOptions: {
    output: {
      manualChunks: { vendor: ['some-lib'] }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The replacement is Rolldown's codeSplitting option4. In practice, we removed our manual chunk config from both projects and let Rolldown handle splitting automatically. The output was comparable — chunk boundaries shifted slightly, but total bundle size stayed similar.

A Couple of Snags

CommonJS Interop: The Quiet Breakage

Vite 8 unifies how default imports from CommonJS modules resolve between dev and production3. Previously, import foo from 'cjs-package' could give you module.exports in dev but module.exports.default in production (or vice versa), depending on the package's export structure.

This bit us on two of eight projects. The symptom: Cannot read properties of undefined on a default import that worked fine in Vite 7. Both cases involved older npm packages that publish ambiguous CommonJS exports.

The fix for each was straightforward — one package had an ESM build we could target with an explicit resolve.alias, and the other we replaced with a maintained alternative. But finding these required a full build-and-smoke-test cycle. Type checking won't catch this; the types are correct, it's the runtime module resolution that changes.

If you hit this and need a quick escape hatch while you sort it out:

// Temporary — buys time to fix the dependency
export default defineConfig({
  legacy: {
    inconsistentCjsInterop: true
  }
});
Enter fullscreen mode Exit fullscreen mode

Don't ship with this flag. It exists to make the migration incremental, not permanent.

Cloudflare Workers: No Surprises (Almost)

The Vite bundler change doesn't affect Workers runtime behavior — your D1 bindings, KV, R2, and service bindings work identically. But build output changes. Chunk filenames and content hashes are different under Rolldown, so if you cache-bust by filename (aren't you?), expect a full cache invalidation on first deploy.

We deployed each project to a preview environment first:

npx wrangler versions upload
Enter fullscreen mode Exit fullscreen mode

All eight projects passed initial testing without Workers-specific issues. The adapter-cloudflare update handles the Vite 8 build output correctly.

While you're in the Cloudflare config, it's a good time to bump compatibility_date in your wrangler.jsonc to the migration date. Not strictly required by the Vite change, but major tooling updates are a natural checkpoint to pick up the latest Workers runtime behavior. In a monorepo, don't forget non-SvelteKit workers — we had seven wrangler.jsonc / wrangler.toml files across the workspace and updated all of them in the same commit. It's easy to miss the scheduler worker or the sync worker that don't touch Vite at all.

CSS Bundle Size: Slightly Larger

Lightning CSS handles syntax lowering differently than esbuild did. Across our projects, CSS bundle sizes increased by 1–3%. Not enough to worry about, but worth knowing. If you're counting CSS bytes, you can switch back temporarily:

export default defineConfig({
  build: {
    cssMinify: 'esbuild' // requires esbuild as a devDependency
  }
});
Enter fullscreen mode Exit fullscreen mode

We didn't bother. The difference is negligible.

Clean Up Dead Overrides

If you had pnpm or npm overrides forcing Rollup to a patched version (e.g., for CVE-2026-27606's path traversal fix), you can remove those — Vite 8 doesn't use Rollup at all. We had a "rollup": ">=4.59.0" override that became dead weight. Non-Rollup overrides — things like cookie, undici, or minimatch pinned for their own CVEs — survive the migration unchanged. Don't touch those.

But don't just remove dead overrides and call it done. Rolldown brings a different transitive dependency tree than Rollup did, which means pnpm audit will surface new findings you didn't have before. Run it after migration. We had to add resolutions for devalue (prototype pollution), minimatch (ReDoS), file-type (infinite loop in ASF parser), and even svelte itself (XSS in SSR contenteditable). Major version bumps are a natural checkpoint for a full security audit — expect to both remove and add overrides. And don't try to review the lockfile diff manually. Ours was over 1,200 lines changed. Trust pnpm audit and pnpm ls vite --recursive instead.

Ghost Lockfiles in Sub-Workers

If your repo has Cloudflare Workers in subdirectories (email workers, cron workers, sync workers), check for stale package-lock.json files sitting alongside pnpm-lock.yaml. Dependabot scans every lockfile it finds, not just the root one. We had a single package-lock.json left over from an early npm install in a worker subdirectory — it was generating 38 Dependabot alerts for vulnerable undici versions that pnpm had already resolved. The fix: delete the stale lockfile, add package-lock.json to the worker's .gitignore, and make sure each worker has its own pnpm.overrides for any transitive vulnerabilities (since pnpm overrides in the root package.json don't cascade into independent worker lockfiles).

Plugin Timing Warnings

After migrating, we noticed Rolldown emitting PLUGIN_TIMINGS warnings in build output, showing what percentage of build time each Vite plugin consumes. Rollup didn't report this. For one of our projects, it showed vite-plugin-sveltekit-guard consuming 71% of total build time, with the actual bundling taking only a fraction. Useful diagnostic data: if your Vite 8 build doesn't feel much faster, check the plugin timings. The Rolldown bundler may be 60% faster, but if plugins dominate your build, the wall-clock improvement will be smaller than the headline numbers suggest.

What to Enable After Migrating

Two new Vite 8 features we found were worth turning on immediately:

server.forwardConsole5 forwards browser console.* calls to your terminal during development. When you're debugging, client-side errors show up alongside server logs without switching to browser devtools. One caveat: this works cleanly with a pure Vite dev server (e.g., pnpm run dev:fast), but if you run through Wrangler for local Cloudflare bindings (e.g., pnpm run dev), the console forwarding can behave inconsistently since Wrangler proxies the dev server. Still worth enabling — just don't be surprised if some messages get swallowed in the Wrangler path.

export default defineConfig({
  server: {
    forwardConsole: true,
  },
});
Enter fullscreen mode Exit fullscreen mode

build.license6 emits a .vite/license.md listing all bundled dependencies' licenses. Low-effort compliance documentation — useful if your org tracks open-source usage.

We skipped resolve.tsconfigPaths5 (since SvelteKit already handles $lib aliases) and devtools: true (it's still alpha).

One tip: we did the migration in two separate commits — first the version bumps and cleanup, then enabling the new features. Separating "make it work" from "make it better" keeps the PR reviewable and makes it easy to bisect if something breaks later.

The Gradual Path

If a direct upgrade surfaces too many issues at once, there's a two-step approach3. Stay on Vite 7 but swap to rolldown-vite to isolate Rolldown-specific behavior:

{
  "devDependencies": {
    "vite": "npm:rolldown-vite@7.2.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Fix any Rolldown issues on the familiar Vite 7 foundation, then upgrade to Vite 8 proper. This separates "Rolldown behavior changes" from "other Vite 8 changes" so you can debug each independently. We didn't need this approach, but it's good to know it exists.

After the Migration

Update your project's documentation to reflect the new tooling, for instance:

Tool Role
Vite 8 Build tool
Rolldown Bundler (replaces esbuild + Rollup)
Oxc JS transforms and minification
Lightning CSS CSS minification

If your SvelteKit guide references rollupOptions, update those references to rolldownOptions. And if you maintain shared configuration across a monorepo, make sure the Vite version constraint is ^8.0.0 in all workspace packages — a single Vite 7 holdout will cause resolution conflicts.

Eight projects migrated to Vite 8 in one afternoon, with no rollbacks. Kudos go to the Vite team for making this a really smooth major upgrade. Build speed improvement is always lovely, and we really love the helpful debug logging. If you're on SvelteKit with @sveltejs/kit@2.53.0 or later, there's no reason to wait.


Originally published at cogley.jp

Rick Cogley is CEO of eSolia Inc., providing bilingual IT outsourcing and infrastructure services in Tokyo, Japan.


  1. Vitest 4.x added Vite 8 support. See the Vitest releases.  

  2. SvelteKit Vite 8 support landed in the 2.53.x series. See What's new in Svelte: March 2026 and the @sveltejs/kit releases.  

  3. See the official Vite v7 to v8 migration guide for full details on CJS interop changes, config renames, manualChunks removal, esbuild.drop replacement, and the gradual rolldown-vite migration path.  

  4. Rolldown manual code splitting documentation.  

  5. See the Vite 8 announcement for server.forwardConsole, resolve.tsconfigPaths, and other new features.  

  6. build.license option documentation

Top comments (0)