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 92.4ms 92.35ms 3.29ms
Next.js 349.4ms 349.17ms 21.18ms
Nuxt 157.6ms 157.64ms 10.1ms
React Router 141.6ms 141.68ms 17.06ms
SolidStart 103ms 102.86ms 13.95ms
SvelteKit 107.4ms 107.49ms 10.22ms
TanStack Start 165.6ms 165.46ms 28.38ms

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 wraps the client-side rendered table in a dynamic import with ssr: false to prevent build-time prerendering
  • TanStack Start, Nuxt, SvelteKit, and SolidStart disable SSR per-route
  • React Router uses route-level clientLoader functions with HydrateFallback so the client-rendered routes are not server-rendered
  • Astro uses client-only React islands for client-side rendered routes
  • Client-side rendered tests use each framework's normal production build because SPA-only build modes are not supported consistently across the frameworks being compared
  • 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 70.2ms 70.15ms 7.68ms
Next.js 146.4ms 146.37ms 18.46ms
Nuxt 94.2ms 94.19ms 11.37ms
React Router 89.4ms 89.21ms 18.54ms
SolidStart 105.4ms 105.41ms 13.47ms
SvelteKit 79.4ms 79.36ms 16.55ms
TanStack Start 117ms 116.9ms 30.27ms

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 625 1.588ms 184.7kb 1.5x
Astro 385 2.527ms 190.65kb 1.5x
Mastro 515 1.912ms 181.95kb 1x
Next.js 41 24.072ms 579.39kb 3.49x
Nuxt 65 13.053ms 298.03kb 2.5x
React Router 156 6.36ms 320.07kb 2.5x
SolidStart 65 13.307ms 381.88kb 2.5x
SvelteKit 356 2.686ms 271.38kb 2.5x
TanStack Start 43 23.043ms 281.55kb 2.5x

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 1,619.6 50 19ms 41ms 98ms 48,121
Astro 686.6 25 44ms 122ms 1984ms 20,376
Next.js 38 5 2169ms 4680ms 4751ms 1,037
Nuxt 68.8 5 2052ms 4121ms 4365ms 2,151
React Router 177.2 10 298ms 1655ms 3947ms 5,823
SolidStart 63 10 3190ms 3736ms 4146ms 2,031
SvelteKit 492 25 80ms 407ms 2452ms 15,637
TanStack Start 44 5 2919ms 4495ms 4537ms 1,345

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

Core Web Vitals Desktop

Good Largest Contentful Paint

Measures how fast a page's main content loads. To provide a good user experience, the LCP should be 2.5 seconds or less.

Good Largest Contentful Paint chart
Framework LCP% CLS% FCP% TTFB% INP%
SolidStart 91 66 91 83 100
Astro 91 87 91 82 97
Nuxt.js 65 57 67 59 95
SvelteKit 79 77 78 71 94
Next.js 72 69 76 67 94
React Router 61 62 66 70 90