Dev Time Run Time e18e.dev Blog

Run Time Stats

SSR Request Throughput

Framework Ops/sec Median Latency Body Size Duplication
Baseline HTML 839 1.202ms 96.81kb 1x
Astro 590 1.697ms 99.86kb 1x
Mastro 510 1.969ms 181.95kb 1x
Next.js 175 5.77ms 199.07kb 2x
Nuxt 381 2.56ms 201.26kb 2x
React Router 133 7.309ms 211.14kb 2x
SolidStart 403 2.482ms 228.11kb 2x
SvelteKit 435 2.231ms 183.55kb 2x
TanStack Start 313 3.148ms 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 92ms 92.09ms 6.8ms
Next.js 349.4ms 349.24ms 17.96ms
Nuxt 96.6ms 96.62ms 11ms
React Router 117.4ms 117.32ms 16.76ms
SolidStart 100.6ms 100.65ms 15.44ms
SvelteKit 115.6ms 115.61ms 10.82ms
TanStack Start 168.2ms 168.05ms 26.2ms

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 72.4ms 72.34ms 0.88ms
Next.js 135.8ms 135.8ms 25.9ms
Nuxt 106.6ms 106.54ms 9.7ms
React Router 160.4ms 160.26ms 16.89ms
SolidStart 96.8ms 96.81ms 15.14ms
SvelteKit 70.6ms 70.5ms 0.93ms
TanStack Start 123.4ms 123.39ms 27.88ms

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.