Dev Time Run Time e18e.dev Blog

Run Time Stats

SSR Load Tests

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,950.8 200 7ms 11ms 25ms 146,571
Astro 670.8 25 62ms 214ms 1970ms 20,474
Next.js 30.6 1 2627ms 4755ms 4884ms 850
Nuxt 65.8 10 2039ms 4119ms 4322ms 2,092
React Router 65 25 2264ms 3714ms 3867ms 2,110
SolidStart 62 5 3225ms 4187ms 4008ms 2,026
SvelteKit 525 25 74ms 328ms 2381ms 16,789
TanStack Start 31.6 5 4032ms 4235ms 4531ms 916

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

SSR Request Throughput

Ops/sec

Ops/sec chart
Framework Ops/sec Median Latency Body Size Duplication
Baseline HTML 823 1.205ms 96.81kb 1x
Astro 571 1.742ms 99.86kb 1x
Mastro 520 1.894ms 181.95kb 1x
Next.js 218 4.756ms 199.11kb 2x
Nuxt 270 3.546ms 201.26kb 2x
React Router 129 7.457ms 211.14kb 2x
SolidStart 402 2.478ms 228.11kb 2x
SvelteKit 441 2.201ms 183.55kb 2x
TanStack Start 308 3.198ms 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

Client Side Rendered Performance

First Paint (ms)

First Paint (ms) chart
Framework First Paint FCP INP
Astro 90.4ms 90.24ms 3.52ms
Next.js 349.6ms 349.95ms 18.66ms
Nuxt 117.8ms 118.06ms 12.79ms
React Router 163.6ms 163.64ms 22.83ms
SolidStart 88ms 88.08ms 15.39ms
SvelteKit 104.8ms 104.85ms 13.71ms
TanStack Start 231.6ms 231.44ms 39.21ms

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.

Server Side Rendered Performance

First Paint (ms)

First Paint (ms) chart
Framework First Paint FCP INP
Astro 85.2ms 85.11ms 0.8ms
Next.js 128.4ms 128.26ms 18.53ms
Nuxt 94.8ms 95.09ms 12.27ms
React Router 160.4ms 160.25ms 15.62ms
SolidStart 101.2ms 101.17ms 16.58ms
SvelteKit 70.8ms 70.85ms 0.59ms
TanStack Start 168.4ms 168.33ms 47.84ms

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.