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 86.8ms 86.79ms 1.95ms
Next.js 348ms 347.96ms 19.92ms
Nuxt 159ms 159.05ms 12.44ms
React Router 122.4ms 122.08ms 18.86ms
SolidStart 97.4ms 97.28ms 18.62ms
SvelteKit 115.2ms 115.09ms 15.19ms
TanStack Start 176ms 176.11ms 29.77ms

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.
  • 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 81.2ms 81.42ms 3.58ms
Next.js 150.2ms 150.09ms 18.23ms
Nuxt 93.4ms 93.51ms 8.98ms
React Router 158ms 158ms 16.89ms
SolidStart 103.2ms 103.13ms 17.86ms
SvelteKit 78.8ms 78.87ms 13.37ms
TanStack Start 115.6ms 115.69ms 29.87ms

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 847 1.201ms 96.81kb 1x
Astro 572 1.738ms 99.86kb 1x
Mastro 515 1.912ms 181.95kb 1x
Next.js 233 4.532ms 199.11kb 2x
Nuxt 385 2.533ms 201.26kb 2x
React Router 130 7.405ms 211.14kb 2x
SolidStart 403 2.465ms 228.11kb 2x
SvelteKit 428 2.279ms 183.55kb 2x
TanStack Start 314 3.15ms 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

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 5,876.4 200 6ms 12ms 25ms 147,665
Astro 668 10 65ms 206ms 2008ms 20,422
Next.js 37.21 5 2198ms 4706ms 4639ms 1,022
Nuxt 69.8 10 1999ms 4035ms 4069ms 2,207
React Router 67.8 10 2180ms 3858ms 4006ms 2,256
SolidStart 59.6 10 3846ms 4122ms 4327ms 1,929
SvelteKit 476 10 80ms 507ms 2505ms 15,072
TanStack Start 43.4 5 3642ms 4392ms 4424ms 1,339

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