Dev Time Run Time e18e.dev Blog

Run Time Stats

Client Side Rendered Tests

First Paint (ms)

First Paint (ms) chart
Framework First Paint FCP INP
Astro 88.2ms 88.17ms 2.18ms
Next.js 347ms 347.05ms 18.04ms
Nuxt 96.8ms 97.1ms 13.84ms
React Router 118ms 117.98ms 17.37ms
SolidStart 94.8ms 94.81ms 17.09ms
SvelteKit 115.6ms 115.69ms 14.48ms
TanStack Start 167.8ms 167.88ms 30.49ms

Methodology

  • Each framework renders a table of 1000 rows with two UUID columns
  • Measured using Lighthouse flow with Chromium via Puppeteer for accurate browser metrics
  • First Paint and First Contentful Paint are measured on initial navigation
  • Interaction to Next Paint is measured by clicking the first row's detail link
  • Benchmarks run 5 times and results are averaged
  • Next.js, TanStack Start, and React Router default to SSR with no per-route opt-out. Next.js wraps the client-side rendered table in a dynamic import with ssr: false to prevent build-time prerendering. TanStack Start uses its built-in spa mode. React Router disables SSR entirely via ssr: false in its config. All other frameworks (Nuxt, SvelteKit, SolidStart, Astro) disable SSR per-route without a separate build.
  • Astro uses React for its client-side rendered test: the benchmark table and detail components are React islands rendered with client:only="react", which prevents Astro from server-rendering those components and lets them render only in the browser. Astro's ClientRouter is not used for this CSR test because it enables client-side transitions and soft navigation behavior rather than client-only rendering.

Server Side Rendered Tests

First Paint (ms)

First Paint (ms) chart
Framework First Paint FCP INP
Astro 67.8ms 68.01ms 2.74ms
Next.js 143ms 143.27ms 18.18ms
Nuxt 92.2ms 91.99ms 10.52ms
React Router 160.4ms 160.44ms 19.17ms
SolidStart 102.8ms 103.02ms 15.97ms
SvelteKit 68.2ms 68.06ms 0.53ms
TanStack Start 128.6ms 128.61ms 35.37ms

Methodology

  • Each framework renders a table of 1000 rows with two UUID columns
  • Measured using Lighthouse flow with Chromium via Puppeteer for accurate browser metrics
  • First Paint and First Contentful Paint are measured on initial navigation
  • Interaction to Next Paint is measured by clicking the first row's detail link
  • Benchmarks run 5 times and results are averaged
  • The measured route is /server-side-rendered, and detail navigation uses /server-side-rendered/:id.

Server Side Throughput Tests

Ops/sec

Ops/sec chart
Framework Ops/sec Median Latency Body Size Duplication
Baseline HTML 846 1.202ms 96.81kb 1x
Astro 587 1.699ms 99.86kb 1x
Mastro 501 1.99ms 181.95kb 1x
Next.js 228 4.594ms 199.11kb 2x
Nuxt 352 2.764ms 201.26kb 2x
React Router 127 7.662ms 211.14kb 2x
SolidStart 405 2.528ms 228.11kb 2x
SvelteKit 434 2.244ms 183.55kb 2x
TanStack Start 317 3.116ms 193.53kb 2x

Methodology

  • Each framework renders a table of 1000 rows with two UUID columns
  • Mock HTTP requests bypass TCP overhead for accurate rendering measurement
  • Data is loaded asynchronously to simulate real-world data fetching
  • Duplication factor indicates how many times each UUID appears in the response (1x = optimal, 2x = includes hydration payload)
  • Benchmarks run for 10 seconds using tinybench
  • Astro, Nuxt, and SvelteKit handle Node.js HTTP requests natively. React Router, SolidStart, and TanStack Start use Web APIs internally, so benchmarks include the cost of their Node.js adapter layers (@react-router/node, h3, and srvx respectively)
  • Next.js defaults to React Server Components (RSC), a different rendering model than traditional server-rendered React. To keep the comparison fair, Next.js uses "use client" to opt out of RSC and use traditional server rendering + hydration like most of the other frameworks
  • Inspired by eknkc/ssr-benchmark

Server Side Load Test

P99 Latency

P99 Latency at 25 Connections

P99 Latency at 25 Connections chart

P90 Latency

P90 Latency at 25 Connections

P90 Latency at 25 Connections chart
Framework Peak req/s Peak Connections P99 @ 25 P99 @ 50 P99 @ 100 Total Req.
Baseline HTML 5,909.2 100 6ms 10ms 22ms 147,464
Astro 672.2 25 61ms 194ms 1973ms 20,636
Next.js 37.6 1 2214ms 4762ms 4759ms 1,009
Nuxt 67.6 10 2099ms 4090ms 4381ms 2,123
React Router 66 10 2300ms 4006ms 3983ms 2,221
SolidStart 60.6 5 3826ms 4289ms 3990ms 1,993
SvelteKit 543.6 10 75ms 343ms 2396ms 16,943
TanStack Start 44.2 10 3882ms 4254ms 4422ms 1,340

Methodology

  • Each framework serves the server-rendered table route over a real local HTTP server
  • The measured route is /server-side-rendered, using the same 1000-row UUID table as the SSR request throughput and browser rendering tests
  • Load is applied in staged connection counts, from 1 through 200 concurrent connections, with each stage running for approximately 5 seconds
  • Peak requests/sec is the highest successful stage throughput observed during the staged run
  • P90 and P99 latency are compared at the 25-, 50-, and 100-connection stages for every framework, so latency is measured under the same concurrency pressure
  • Total requests cover the full staged load run, not only the peak stage