Run Time Stats
SSR Performance
Measured on GitHub Actions (ubuntu-latest, Node 24) using custom SSR benchmark apps.
| Framework | Ops/sec | Median Latency | Body Size | Duplication |
|---|---|---|---|---|
| Baseline HTML | 739 | 1.348ms | 96.81kb | 1x |
| Astro | 372 | 2.668ms | 99.86kb | 1x |
| Mastro | 320 | 3.046ms | 181.95kb | 1x |
| Next.js | 133 | 7.543ms | 199.07kb | 2x |
| Nuxt | 254 | 3.767ms | 201.26kb | 2x |
| React Router | 64 | 0ms | 211.14kb | 2x |
| SolidStart | 251 | 3.92ms | 227.79kb | 2x |
| SvelteKit | 277 | 3.457ms | 183.55kb | 2x |
| TanStack Start | 188 | 5.165ms | 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 | 110.8ms | 110.87ms | 10.73ms |
| Next.js | 384.2ms | 384.33ms | 20.37ms |
| Nuxt | 149.2ms | 149.15ms | 17.51ms |
| React Router | 146.8ms | 146.85ms | 16.23ms |
| SolidStart | 152.8ms | 152.82ms | 27.27ms |
| SvelteKit | 149.2ms | 149.42ms | 13.9ms |
| TanStack Start | 803.2ms | 803.35ms | 195.35ms |
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 | 106.4ms | 106.14ms | 0.95ms |
| Next.js | 174.8ms | 174.68ms | 15.97ms |
| Nuxt | 112.6ms | 112.61ms | 3.63ms |
| React Router | 209.6ms | 209.68ms | 3.03ms |
| SolidStart | 90.8ms | 90.86ms | 17.91ms |
| SvelteKit | 100.8ms | 100.62ms | 1.02ms |
| TanStack Start | 124.2ms | 124ms | 5.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
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.