Run Time Stats
SSR Performance
Measured on GitHub Actions (ubuntu-latest, Node 24) using custom SSR benchmark apps.
| Framework | Ops/sec | Avg Latency | Body Size | Duplication |
|---|---|---|---|---|
| Baseline HTML | 766 | 1.315ms | 96.81kb | 1x |
| Astro | 385 | 2.628ms | 99.86kb | 1x |
| Mastro | 339 | 2.983ms | 181.95kb | 1x |
| Next.js | 125 | 8.117ms | 199.11kb | 2x |
| Nuxt | 250 | 4.076ms | 201.26kb | 2x |
| React Router | 64 | 15.528ms | 211.14kb | 2x |
| SolidStart | 263 | 4.066ms | 227.79kb | 2x |
| SvelteKit | 276 | 3.725ms | 183.55kb | 2x |
| TanStack Start | 179 | 5.665ms | 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
First Contentful Paint (ms)
Default
Interaction to Next Paint (ms)
Default
Measured on GitHub Actions (ubuntu-latest, Node 24) using Lighthouse flow with Chromium.
| Framework | First Paint | FCP | INP |
|---|---|---|---|
| Astro | 136.6ms | 136.39ms | 11.19ms |
| Next.js | 384.6ms | 384.44ms | 21.35ms |
| Nuxt | 148.4ms | 148.24ms | 14.42ms |
| React Router | 178.8ms | 178.92ms | 16.54ms |
| SolidStart | 127.6ms | 127.58ms | 20.09ms |
| SvelteKit | 156.8ms | 156.99ms | 12.88ms |
| TanStack Start | 804.2ms | 804.32ms | 199.32ms |
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
dynamicimport withssr: falseto prevent build-time prerendering. TanStack Start uses its built-in spa mode. React Router disables SSR entirely viassr: falsein its config. All other frameworks (Nuxt, SvelteKit, SolidStart, Astro) disable SSR per-route without a separate build.
MPA Performance
First Paint (ms)
Default
First Contentful Paint (ms)
Default
Interaction to Next Paint (ms)
Default
Measured on GitHub Actions (ubuntu-latest, Node 24) using Lighthouse flow with Chromium.
| Framework | First Paint | FCP | INP |
|---|---|---|---|
| Astro | 100.8ms | 100.78ms | 1.03ms |
| Next.js | 171.2ms | 171.37ms | 19.13ms |
| Nuxt | 111.2ms | 111.34ms | 3.66ms |
| React Router | 207.6ms | 207.4ms | 2.44ms |
| SolidStart | 126.8ms | 126.7ms | 17.68ms |
| SvelteKit | 98.8ms | 98.69ms | 3.27ms |
| TanStack Start | 99.8ms | 99.92ms | 2ms |
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
dynamicimport withssr: falseto prevent build-time prerendering. TanStack Start uses its built-in spa mode. React Router disables SSR entirely viassr: falsein its config. All other frameworks (Nuxt, SvelteKit, SolidStart, Astro) disable SSR per-route without a separate build.