Dev Time Run Time e18e.dev Blog

Run Time Stats

SSR Performance

Framework Ops/sec Median Latency Body Size Duplication
Baseline HTML 853 1.198ms 96.81kb 1x
Astro 564 1.766ms 99.86kb 1x
Mastro 499 1.999ms 181.95kb 1x
Next.js 230 4.582ms 199.11kb 2x
Nuxt 386 2.511ms 201.26kb 2x
React Router 64 0ms 211.14kb 2x
SolidStart 407 2.537ms 227.79kb 2x
SvelteKit 431 2.256ms 183.55kb 2x
TanStack Start 314 3.141ms 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

SPA Performance

First Paint (ms)

First Paint (ms) chart
Framework First Paint FCP INP
Astro 98.6ms 98.73ms 3.34ms
Next.js 347.4ms 347.62ms 22.5ms
Nuxt 95.4ms 95.64ms 14.14ms
React Router 123.4ms 123.42ms 20.58ms
SolidStart 91.4ms 91.41ms 15.82ms
SvelteKit 114ms 113.98ms 10.22ms
TanStack Start 670.8ms 670.84ms 110.55ms

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 69ms 69.01ms 0.63ms
Next.js 121.8ms 121.61ms 12.61ms
Nuxt 78ms 77.94ms 0.48ms
React Router 135.6ms 135.57ms 2.24ms
SolidStart 76.6ms 76.68ms 15.78ms
SvelteKit 70.2ms 70.19ms 0.57ms
TanStack Start 88.6ms 88.31ms 1.93ms

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.