framework-bench

The same small blog, built 4 ways, measured by one harness

Same content, same page, same SEO head on every target — the only thing that varies is the framework you reach for, and that is exactly what gets weighed.

Generated · median of 5 runs, one warm run discarded · re-run with bun run bench

163× the JavaScript Next.js ships versus the lightest non-zero stack (Astro) — for the identical interaction: a text + tag filter on the index page.

JS shipped for the same page

Index (/) — the one measured interaction (text + tag filter)
native / BATCH
2kb
native + DPU
3kb
Astro
744b (inline)
Next.js
118kb (inline)
Article (/posts/the-browser-grew-up) — static page, no interaction
native / BATCH
0
native + DPU
0
Astro
0
Next.js
109kb (inline)

That spread is the whole thesis: the interaction is the same; the runtime each stack makes you ship for it is not. Astro's figure is its filter script inlined into the HTML; native's is one small external module — both are essentially the filter logic and nothing else. Next's is React runtime + hydration.

How to read this (the honest frame)

Full numbers — index (/)

TargetJS shippedWireRequestsRender-blockingTTFB (med)Load (med)
native / BATCH2kb14kb31css/0js4ms12ms
native + DPU3kb15kb31css/0js3ms10ms
Astro744b (744b inline)5kb21css/0js11ms38ms
Next.js118kb (16kb inline)109kb71css/1js9ms128ms

Full numbers — article

TargetJS shippedWireRequestsRender-blockingTTFB (med)Load (med)
native / BATCH07kb21css/0js3ms9ms
native + DPU07kb21css/0js3ms9ms
Astro03kb21css/0js4ms12ms
Next.js109kb (7kb inline)106kb61css/1js4ms66ms

The facts the auditor can't measure

TargetBuild stepRuntime deps (direct)node_modulesProduction mode measured
native / BATCHNo1 (@tjakoen/batch)47Mbun server.ts — no build step (server IS the production artifact)
native + DPUNo1 (@tjakoen/batch)47Mbun server.ts — no build step; filter swaps a server fragment via native setHTMLUnsafe()/streamHTMLUnsafe()
AstroYes1 (astro)190Mastro build → astro preview (static output)
Next.jsYes3 (next, react, react-dom)335Mnext build && next start (client-component page is not pure SSG — see note)

Parity — proof the comparison is fair

The same head on every target: index first, article second.

TargetTitleDescCanonicalOG/Twitter1×H1JSON-LDhtml langSemantics
native / BATCH
native + DPU
Astro
Next.js
TargetTitleDescCanonicalOG/Twitter1×H1JSON-LDhtml langSemantics
native / BATCH
native + DPU
Astro
Next.js

Honest notes

Run it yourself

git clone https://github.com/tjakoen/framework-bench.git
cd framework-bench && bun install
bun run bench

Perf is corroboration. The bytes, the request count, and the build-step column are the argument.