Dev Time Run Time e18e.dev Blog

Run Time Stats

SSR Performance

Framework Ops/sec Median Latency Body Size Duplication
Baseline HTML 856 1.198ms 96.81kb 1x
Astro 565 1.759ms 99.86kb 1x
Mastro 492 2.023ms 181.95kb 1x
Next.js 230 4.548ms 199.11kb 2x
Nuxt 383 2.543ms 201.26kb 2x
React Router 64 0ms 211.14kb 2x
SolidStart 396 2.48ms 227.79kb 2x
SvelteKit 440 2.217ms 183.55kb 2x
TanStack Start 308 3.202ms 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)

Default
  • Framework First Paint FCP INP
    Astro 91.6ms 91.64ms 6.33ms
    Next.js 349.2ms 349.19ms 15.14ms
    Nuxt 93.4ms 93.52ms 10.3ms
    React Router 118.4ms 118.33ms 17.87ms
    SolidStart 93.4ms 93.28ms 12.27ms
    SvelteKit 108.4ms 108.41ms 16.73ms
    TanStack Start 663.2ms 663.29ms 110.97ms

    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)

    Default
  • Framework First Paint FCP INP
    Astro 68.8ms 68.6ms 0.49ms
    Next.js 142.2ms 142.21ms 16.08ms
    Nuxt 84.6ms 84.64ms 0.63ms
    React Router 134.8ms 134.94ms 2.72ms
    SolidStart 87.4ms 87.32ms 16.3ms
    SvelteKit 72.6ms 72.66ms 0.48ms
    TanStack Start 81.4ms 81.42ms 2.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
    • 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.