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 | 708 | 1.422ms | 96.81kb | 1x |
| Astro | 376 | 2.681ms | 99.86kb | 1x |
| Mastro | 255 | 3.951ms | 181.95kb | 1x |
| Next.js | 138 | 7.371ms | 199.11kb | 2x |
| Nuxt | 234 | 4.365ms | 201.26kb | 2x |
| React Router | 64 | 15.528ms | 211.14kb | 2x |
| SolidStart | 259 | 4.103ms | 227.79kb | 2x |
| SvelteKit | 271 | 3.79ms | 183.55kb | 2x |
| TanStack Start | 191 | 5.276ms | 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 | 139.4ms | 139.47ms | 8.87ms |
| Next.js | 380.8ms | 380.98ms | 20.58ms |
| Nuxt | 141.2ms | 141.31ms | 15.77ms |
| React Router | 181ms | 180.84ms | 17.9ms |
| SolidStart | 128.4ms | 128.57ms | 20.63ms |
| SvelteKit | 142ms | 141.8ms | 15.65ms |
| TanStack Start | 829.4ms | 829.42ms | 228.39ms |
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.2ms | 100.3ms | 0.91ms |
| Next.js | 190ms | 189.73ms | 17.51ms |
| Nuxt | 106ms | 105.98ms | 6.45ms |
| React Router | 202.2ms | 202.07ms | 2.74ms |
| SolidStart | 111.6ms | 111.89ms | 16.42ms |
| SvelteKit | 107.4ms | 107.51ms | 3.41ms |
| TanStack Start | 122.8ms | 122.75ms | 8.36ms |
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.