React Router
Dev Time Performance
Measured using pnpm on GitHub Actions (ubuntu-latest, Node 24) based on the starter project set up by each framework's CLI.
| Prod Deps | Dev Deps | Dup. Deps | node_modules | node_modules (prod) | Dep Install Size | Graph |
|---|---|---|---|---|---|---|
| 6 | 8 | 9 | 106.87MB | 17.27MB | 91.64MB | View |
| Metric | Avg | Min | Max |
|---|---|---|---|
| Install | 1.60s | 1.49s | 1.74s |
| Cold Build | 2.78s | 2.75s | 2.81s |
| Warm Build | 2.80s | 2.77s | 2.82s |
Build output size: 0.38MB
Duplicate Dependencies
9 duplicate dependencies detected across this starter's node_modules.
View 9 duplicate dependencies
- debug
- semver
- pathe
- negotiator
- safe-buffer
- on-finished
- mime-db
- ms
- cookie
Runtime Performance
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 |
| React Router | 64 | 15.528ms | 211.14kb | 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
Measured on GitHub Actions (ubuntu-latest, Node 24) using Lighthouse flow with Chromium.
| Framework | First Paint | FCP | INP |
|---|---|---|---|
| React Router | 181ms | 180.84ms | 17.9ms |
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
Measured on GitHub Actions (ubuntu-latest, Node 24) using Lighthouse flow with Chromium.
| Framework | First Paint | FCP | INP |
|---|---|---|---|
| React Router | 202.2ms | 202.07ms | 2.74ms |
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.