Vite Full Bundle Dev Mode (experimental.bundledDev)

Vite 8 introduces an experimental mode that flips the dev server’s fundamental architecture: instead of serving unbundled ESM modules one at a time, it bundles everything upfront using Rolldown and serves the result from memory.

Why This Exists

Vite’s unbundled ESM approach (see vite-dev-server.md) works beautifully for small-to-medium projects. But at scale, it hits real problems:

Unbundled ESM for a large app:

Browser loads index.html
  -> imports App.tsx
    -> imports Router, Store, Layout, Theme, ...
      -> each imports 10-20 more modules
        -> each of those imports more

Result: 200-500 individual HTTP requests on page load
Each request: DNS lookup, connection, transform, response
Waterfall: can't request Button.tsx until Layout.tsx arrives

The browser’s module loader becomes the bottleneck. Pre-bundling dependencies helps (React becomes one file), but your source code is still hundreds of individual modules. For apps with 500+ components, initial dev page load can take 5-10 seconds just from network waterfall.

Other pain points:

  • CORS complexity - each module request needs proper headers; cross-origin setups (iframes, workers) get messy
  • Dev/prod divergence - dev serves unbundled ESM, production serves bundled code. Different module graphs, different execution order, different bugs
  • Browser module limits - some browsers throttle concurrent module requests

How to Enable

// vite.config.ts
export default defineConfig({
  experimental: {
    bundledDev: true
  }
})

That is it. Vite switches from its unbundled transform-on-demand pipeline to Rolldown’s native dev engine.

Architecture

Traditional Vite Dev:
  Browser requests module -> transform single file -> serve it
  Browser requests next import -> transform -> serve
  (repeat 200+ times per page load)

Bundled Dev:
  Rolldown bundles all modules -> output stored in memory
  Browser requests page -> serve pre-built bundle (1-3 files)
  File changes -> Rolldown incremental rebuild -> update in memory

The key components:

  1. Rolldown’s native dev engine - not a full production build on every change. Rolldown maintains an internal module graph and performs incremental rebuilds. Only modules affected by a change are reprocessed.

  2. MemoryFiles class - output files (bundles, assets) live in memory, not on disk. No filesystem I/O during dev. The class supports lazy loading: chunks that haven’t been requested yet are not fully materialized until the browser asks for them.

  3. memoryFilesMiddleware - a Connect middleware that intercepts requests and serves files from the MemoryFiles store instead of going through the traditional transform pipeline. It handles content-type detection, cache headers, and source map serving.

Request flow in bundled mode:

Browser: GET /src/main.js
         |
         v
  memoryFilesMiddleware
    - Check MemoryFiles store
    - If found: serve directly (fast path)
    - If not found: fall through to other middleware
         |
         v
  Response with bundled output
  (contains App + Router + Store + all deps in one file)

Performance

The claimed improvements for large applications:

MetricUnbundled ESMBundled DevImprovement
Dev startup~3s~1s3x faster
Full page reload~2.5s~1.5s40% faster
Network requests200-50010-2010x fewer

These numbers depend heavily on project size. Small projects (under 50 modules) may see minimal difference or even slight regression, since the upfront bundling cost can exceed the per-request transform cost.

HMR in Bundled Mode

Hot module replacement works differently when the dev server serves bundles:

File changed on disk
         |
         v
  Rolldown incremental rebuild
  - Reprocess only affected modules
  - Produce new chunk(s) with updates
  - Update MemoryFiles store
         |
         v
  WebSocket message to browser (same protocol as unbundled)
  { type: "update", updates: [...] }
         |
         v
  Browser fetches updated chunk from memoryFilesMiddleware
         |
         v
  Framework HMR handler (React Refresh, etc.) applies update

The WebSocket protocol is the same. The browser-side HMR client does not know whether it is receiving an unbundled module or a bundled chunk. The difference is entirely server-side.

Current Limitations

  • Client environment only - SSR and other server-side environments still use the unbundled pipeline
  • Experimental - the API surface may change between Vite minor versions
  • Plugin compatibility - plugins that assume one-module-per-request may behave differently. Most Vite plugins work fine, but plugins that inspect or modify the request/response cycle directly may need updates.
  • Source maps - mapping bundled output back to source files adds complexity. The MemoryFiles class handles this, but edge cases in deeply nested source maps can surface.

What This Means for the Toolchain

This is the convergence story VoidZero has been building toward:

Before (Vite 5-7):
  Dev:  esbuild (dep pre-bundling) + on-demand transforms
  Prod: Rollup (full bundle)
  Gap:  different bundlers, different behavior, different bugs

After (Vite 8 with bundledDev):
  Dev:  Rolldown (incremental bundles from memory)
  Prod: Rolldown (full bundle to disk)
  Gap:  same bundler, same module resolution, same output format

Dev/prod consistency has been Vite’s biggest pragmatic weakness. The same import order, the same chunk boundaries, the same tree-shaking behavior in dev and prod means fewer “works in dev, breaks in prod” surprises.

The dependency pre-bundling step (esbuild converting node_modules to ESM) also becomes unnecessary in bundled mode - Rolldown handles everything in a single pass, including CommonJS interop.

Connection to Rolldown’s Dev Engine

The bundled dev mode is built on Rolldown’s experimental_dev feature, which exposes incremental bundling as a first-class capability rather than bolting it on top of a production-oriented build system. This is why the MemoryFiles abstraction exists at the Rolldown level rather than being a Vite-only concept - it is designed to be reusable across the VoidZero toolchain.