
What Cloudflare's VoidZero Report Says About Oxc, Rolldown, and Vite Build Times
Introduction
Cloudflare published a retrospective on 2026-09-28 titled "Four months of VoidZero at Cloudflare: making the open-source JavaScript toolchain faster for all humans and agents," and it is worth reading for one reason more than any other. The speedup itself — the thing the headline implies — is not the interesting part. The word agents is. Below I'll separate what the report actually states from what it only implies, walk through the Oxc and Rolldown layers it touches, and show how to benchmark a Vite build cold so you can tell whether any of it applies to your project.
Let me put my position up front. The Rust layer under Vite is real work, and pushing parse, transform, minify, and link into Oxc and Rolldown is the biggest structural change to JavaScript builds since esbuild made everyone care about cold starts. But "faster for agents" is a cold-build and CI argument, not an interactive one. It's also the claim teams are most likely to measure badly — on a warm dev server, on a laptop, on a project whose build time is dominated by JavaScript plugins Rust never touches.
What the Cloudflare VoidZero Report Actually Covers
The four-month timeline and which teams did the work
The published item is confirmed: a Cloudflare Blog post dated 2026-09-28, a retrospective on four months of collaboration between Cloudflare and VoidZero, scoped to speeding up the open-source JavaScript toolchain, with the work landing in the Rust tooling layer that increasingly underpins Vite and downstream frameworks. VoidZero builds Vite, Rolldown, and Oxc, so "collaborating with VoidZero" means contributing upstream rather than shipping a private Cloudflare fork. That last part is my inference from who VoidZero is — the summary doesn't say it outright.
What the summary doesn't name: individual engineers, which Cloudflare org owned the work, which repositories took the commits, or a single speedup number. I'm not going to guess at those.
Separating what the post states from what it implies
| Claim | Status |
|---|---|
| Four months of Cloudflare/VoidZero work on the OSS JS toolchain | Confirmed (publisher, title, date) |
| Work touches the Rust layer under Vite and downstream frameworks | Confirmed (summary) |
| Framing targets "humans and agents" as distinct beneficiaries | Confirmed (title) |
| Specific percentage or millisecond improvements | Not available in what I have — unverified |
| Which Vite release train ships which piece by default | Unverified; check release notes |
| Agent workloads were a design input rather than a framing device | Implied, not proven |
That last row is the one to watch. A title can describe a workload without that workload having driven a single technical decision.
I'm working from the Cloudflare Blog headline, publication date, and summary surfaced through a news feed — not the full post body. Anything below that describes Oxc, Rolldown, or Vite mechanics comes from those projects' own documentation, and I've marked it as such.
The Two Rust Layers Under Vite: Oxc and Rolldown
Oxc as the parser, transformer, and lint core
Oxc is a Rust toolchain: parser, TypeScript/JSX transformer, minifier, and linter (oxlint). The parser exposes an ESTree-compatible AST across a Node-API boundary, and that detail carries weight — a JavaScript caller can consume native parse output while the rest of the pipeline stays JS. Rolldown skips the boundary entirely and consumes Oxc in-process, file by file.
Rolldown as the bundler replacing the Rollup path
Rolldown is a Rust bundler built on Oxc, and it deliberately targets Rollup-compatible semantics, plugin API included. That compatibility goal is the only reason adoption is plausible: Vite's plugin ecosystem is Rollup-shaped, and a bundler that ignored resolveId/load/transform would orphan all of it.
In the Rolldown-backed Vite variant, both production bundling and dependency pre-bundling (optimizeDeps, historically esbuild's job) move onto Rolldown. Two native engines collapse into one. That's the structural change.
What Vite itself still owns after those layers move to Rust
The dev server, module graph, plugin container, HMR, config resolution, and SSR transform pipeline all stay in Vite. They're JavaScript, and they aren't going anywhere soon — the module graph is mutable state shared with plugins, and plugins are JS by contract.
So the accurate mental model isn't "Vite is now Rust." It's "Vite is a JavaScript orchestrator over Rust primitives," and the profile now piles up in the orchestrator.
Why "Faster for Humans and Agents" Is a Different Performance Bar
Cold builds, warm builds, and who pays for each one
| Loop | What dominates | Who notices |
|---|---|---|
| Human editing | HMR, module graph updates, warm dep cache | The developer, in milliseconds |
| Human pre-commit | Partial rebuild, typecheck | The developer, occasionally |
| Agent editing in CI | Full cold install + full cold build | The whole pipeline, in minutes |
A human iterating locally gets a warm node_modules/.vite dependency cache and a long-lived server. An agent that writes files, runs a build, reads the output, and writes more files usually gets the opposite: a fresh container, empty caches, and a full bundle every cycle. The same absolute milliseconds buy wildly different amounts of progress in each loop.
Agent-driven edits break the caching assumptions build tools were designed around
Build caches assume incremental, localized change. Agents break that in specific ways:
- Broad invalidation. A refactor across 40 files changes the shape of the module graph, not one module.
- No warm server. Many agent harnesses shell out to
vite buildper iteration instead of talking to a dev server. - Regenerated files. Formatters and codegen rewrite files that "didn't change," which busts mtime-keyed caches.
- Ephemeral filesystems. On a Docker layer or a fresh CI runner, everything under
node_modules/.vitestarts cold every time, which makes warm performance improvements worth nothing.
So "faster for agents," read honestly, is an argument that cold-start performance is now a first-class product requirement. A slow cold build costs an agent a timeout or a truncated verification loop, and it costs CI wall-clock minutes on every push.
Measuring Vite Build Times Without Fooling Yourself
A minimal reproducible harness and what to record
Reach for hyperfine so you get distributions instead of vibes, and make the prepare step delete the cache you actually care about:
## bench-cold.sh — cold-cache production build, 5 runs
hyperfine \
--warmup 1 \
--runs 5 \
--prepare 'rm -rf node_modules/.vite dist' \
'pnpm exec vite build'
Output shape (numbers are yours to fill in — I'm not inventing timings):
Benchmark 1: pnpm exec vite build
Time (mean ± σ): <cold-ms> ± <σ-ms> [User: <u-ms>, System: <s-ms>]
Range (min … max): <min-ms> … <max-ms> 5 runs
Record next to it: Node version, package manager version, plugin count, source file count, and whether the run happened on your machine or a shared runner. A timing without those isn't a result.
Then attribute the time, because the aggregate hides where your ceiling is. Here's a small script that takes phase timings and tells you how much is even eligible for the Rust layer:
const phases = { resolve: 900, parse: 2400, transform: 3800, pluginHooks: 6100, bundle: 2700, minify: 1500, write: 600 };
const rs = phases.parse + phases.transform + phases.bundle + phases.minify;
const total = Object.values(phases).reduce((a, b) => a + b, 0);
console.log({ total, rsEligible: rs, rsShare: `${(rs / total * 100).toFixed(1)}%`, jsPlugins: `${(phases.pluginHooks / total * 100).toFixed(1)}%` });
{ total: 18000, rsEligible: 10400, rsShare: '57.8%', jsPlugins: '33.9%' }
That's arithmetic on illustrative inputs, not a measurement of your project — but the shape of the conclusion is the point. If plugin hooks are a third of your build, a Rust layer that becomes infinitely fast still caps you at roughly 58%.
Conditions that invalidate a benchmark result
- Comparing a cold build with
rm -rf node_modules/.viteagainst a warm one. - Running on a shared CI runner with noisy neighbors, or with remote build caching (Turborepo, Nx) enabled on one arm only.
- Changing plugin sets,
optimizeDeps.include, or output targets between arms. - Laptop thermal throttling across a long run — randomize the order or benchmark on a fixed machine.
- Reporting a single run instead of a distribution.
- Comparing against a different Vite major version and blaming the delta on Rolldown.
Where the Oxc and Rolldown Gains Are Real and Where They Are Not
Parse and transform costs that move to native code
Per-file parse, TypeScript stripping, the JSX transform, minification, and linking are CPU-bound, embarrassingly parallel, and free of a per-node JS boundary once the pipeline is native. On a large TypeScript app, that's where the repeatable wins live — and they show up loudest in exactly the case agents create: a full cold build with an empty cache.
The JavaScript plugin layer Rust never touches
The plugin container still calls your hooks in JS. Every transform call crosses the boundary, and the hook receives and returns JS values. Plugins doing heavy AST work in JS — i18n extraction, CSS-in-JS, GraphQL transforms, custom codegen — keep their entire cost.
The practical implication: audit your plugin time first. If plugins dominate, adopting a Rust bundler is a rounding error until you shrink them or push them native.
Trying the Rolldown-Backed Vite Path in Your Own Project
Adopting the Rust-backed Vite path without breaking CI
The documented approach for the opt-in variant is aliasing vite onto the Rolldown-backed package instead of importing it directly, so every existing vite invocation picks it up:
{
"overrides": {
"vite": "npm:rolldown-vite@latest"
}
}
Confirm the exact field for your package manager (pnpm.overrides, Yarn resolutions), and pin a concrete version rather than @latest before you commit. Then run it as a non-blocking CI job for a couple of weeks before it gates merges:
## compat job — allowed to fail while you evaluate
pnpm install --no-frozen-lockfile
pnpm exec vite build
Compatibility checks to run before committing to it
- Enumerate the hooks your plugins implement:
grep -rnoE '\b(buildStart|resolveId|load|transform|renderChunk|generateBundle|augmentChunkHash|moduleParsed|buildEnd|closeBundle)\b' \
vite.config.* src/plugins/ 2>/dev/null | sort -u
- Look for plugins that reach into Rollup internals —
this.getModuleInfo,this.emitFile, assumptions aboutoutput.format, or anything typed against Rollup'sPluginContext. - Check native or WASM addons that assume a specific bundler.
- Diff
optimizeDeps.include/excludebehavior, since pre-bundling is one of the moving pieces. - Diff the produced
dist/against your current output — file names, chunk splitting, and asset hashes are not guaranteed identical.
Chunk splitting and asset hashing changes between bundlers can silently break
things that reference hashed filenames — service worker precache manifests,
CDN purge rules, and inline import.meta.url asset paths. Diff the output
directory, don't just check that the build exits 0.
Open Questions the Cloudflare VoidZero Report Leaves Unanswered
- How much of the measured improvement comes from Oxc, how much from Rolldown, and how much from Vite-level orchestration? One aggregate number hides the split.
- Does the post carry agent-specific measurements — cold CI builds, agent iteration loops — or is "agents" a framing layer over human-facing numbers?
- How deep does Rollup plugin-API compatibility actually go for the long tail, versus the popular dozen plugins?
- What's the sustainability model for a company-backed Rust toolchain the whole ecosystem now leans on? Concentration risk is an engineering concern, not a governance footnote.
- Which Vite release makes the Rolldown path the default, and what's the migration story for
optimizeDepsedge cases?
Conclusion
The Rust layer under Vite is the right investment, and Cloudflare spending four months on Oxc and Rolldown helps the ecosystem more than another internal build system would. But benchmark this on your warm dev server and you'll conclude nothing changed. You'll be right — for that loop.
Measure the cold path instead. Delete node_modules/.vite, run five builds, record the phase attribution. If your plugin hooks are a third of the total, your realistic ceiling is around a 60% improvement no matter how fast Rust gets, and the next win lives in your plugins. If your cold CI build is the bottleneck, this is the most consequential toolchain shift in years, and the early-access job is worth standing up this week.
Further Reading
- Oxc project documentation — parser, transformer, minifier, and linter
- Rolldown documentation — Rust bundler and Rollup plugin-API compatibility goals
- Vite: Rolldown guide — the opt-in Rust-backed Vite path
- rolldown/rolldown on GitHub — source and release notes
- Cloudflare Blog item: "Four months of VoidZero at Cloudflare" (2026-09-28) — syndicated link I worked from; find the original at blog.cloudflare.com for the full text


