Dev Time Run Time e18e.dev Blog

Run Time Stats

SSR Performance

Framework Ops/sec Median Latency Body Size Duplication
Baseline HTML 848 1.202ms 96.81kb 1x
Astro 570 1.745ms 99.86kb 1x
Mastro 502 1.986ms 181.95kb 1x
Next.js 215 4.812ms 199.11kb 2x
Nuxt 384 2.544ms 201.26kb 2x
React Router 64 0ms 211.14kb 2x
SolidStart 404 2.478ms 227.77kb 2x
SvelteKit 437 2.221ms 183.55kb 2x
TanStack Start 312 3.163ms 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 SSR. To keep the comparison fair, Next.js uses "use client" to opt out of RSC and use traditional SSR + 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 92.4ms 92.3ms 6.29ms
Next.js 348.6ms 348.55ms 16.28ms
Nuxt 92.2ms 92.33ms 10.13ms
React Router 128.6ms 128.5ms 15.03ms
SolidStart 92.2ms 91.91ms 14.89ms
SvelteKit 106.6ms 106.59ms 13.96ms
TanStack Start 144.6ms 144.81ms 26.14ms

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 SPA 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.

MPA Performance

First Paint (ms)

First Paint (ms) chart
Framework First Paint FCP INP
Astro 68.8ms 68.87ms 3.41ms
Next.js 124.2ms 124.06ms 0ms
Nuxt 96.2ms 96.23ms 13.72ms
React Router 141ms 140.92ms 2.86ms
SolidStart 80ms 79.87ms 11.96ms
SvelteKit 72.4ms 72.44ms 0.45ms
TanStack Start 81.2ms 81.16ms 3.36ms

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 SPA 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.