Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/validate-stats.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ jobs:
echo "Building client-side rendered app for $PKG..."
(cd packages/$PKG && pnpm build)
echo "Running client-side rendered benchmark for $PKG..."
pnpm --filter @framework-tracker/stats-generator run:client-side-rendered $PKG
pnpm --filter @framework-tracker/stats-generator run:client-side-rendered $PKG 1
done

echo ""
Expand All @@ -120,7 +120,7 @@ jobs:
echo "Building server-side rendered app for $PKG..."
(cd packages/$PKG && pnpm build)
echo "Running server-side rendered benchmark for $PKG..."
pnpm --filter @framework-tracker/stats-generator run:server-side-rendered $PKG
pnpm --filter @framework-tracker/stats-generator run:server-side-rendered $PKG 1
done

echo ""
Expand Down
21 changes: 14 additions & 7 deletions packages/app-solid-start/ci-stats.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
{
"timingMeasuredAt": "2026-08-08T23:59:59.029Z",
"runner": "Depot depot-ubuntu-24.04 GitHub Actions runner (2 CPUs, 8 GB RAM, 100 GB disk, 2 GB disk accelerator)",
"timingMeasuredAt": "2026-08-09T11:19:10.269Z",
"runner": "local",
"frameworkVersion": "2.0.0",
"clientSideRenderedTests": {
"firstPaintMs": 163.8,
"fcpMs": 163.92,
"inpMs": 0,
"runs": 5
"firstPaintMs": 112,
"fcpMs": 111.68,
"interactionTests": {
"scenario": "first-row-detail-navigation",
"source": "lighthouse-inp-breakdown",
"interactionLatencyMs": 23.04,
"inputDelayMs": 0.5,
"processingDurationMs": 1.9,
"presentationDelayMs": 20.64
},
"runs": 1
},
"ssrRequestThroughputTests": {
"opsPerSec": 429,
Expand Down Expand Up @@ -151,5 +158,5 @@
},
"devDependencies": {}
},
"browserVersion": "Google Chrome 151.0.7922.75"
"browserVersion": "Google Chrome 151.0.7922.77"
}
2 changes: 0 additions & 2 deletions packages/docs/src/components/ClientSideRenderedCharts.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
---
import ClientSideRenderedFPChart from './ClientSideRenderedFPChart.astro'
import ClientSideRenderedFCPChart from './ClientSideRenderedFCPChart.astro'
import ClientSideRenderedINPChart from './ClientSideRenderedINPChart.astro'
---

<ClientSideRenderedFPChart />
<ClientSideRenderedFCPChart />
<ClientSideRenderedINPChart />
15 changes: 0 additions & 15 deletions packages/docs/src/components/ClientSideRenderedINPChart.astro

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const columns = [
},
{ key: 'firstPaintMs', header: 'First Paint' },
{ key: 'fcpMs', header: 'FCP' },
{ key: 'inpMs', header: 'INP' },
]

const tableData = clientSideRenderedStats
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,3 @@ const { versions } = Astro.props
valueFormat="ms"
yAxisLabel="Milliseconds"
/>
<VersionLineChart
title="Client INP"
versions={versions}
metric="clientSideRenderedTests.inpMs"
valueFormat="ms"
yAxisLabel="Milliseconds"
/>
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,3 @@ const { versions } = Astro.props
valueFormat="ms"
yAxisLabel="Milliseconds"
/>
<VersionLineChart
title="Server INP"
versions={versions}
metric="serverSideRenderedTests.inpMs"
valueFormat="ms"
yAxisLabel="Milliseconds"
/>
2 changes: 0 additions & 2 deletions packages/docs/src/components/ServerSideRenderedCharts.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
---
import ServerSideRenderedFPChart from './ServerSideRenderedFPChart.astro'
import ServerSideRenderedFCPChart from './ServerSideRenderedFCPChart.astro'
import ServerSideRenderedINPChart from './ServerSideRenderedINPChart.astro'
---

<ServerSideRenderedFPChart />
<ServerSideRenderedFCPChart />
<ServerSideRenderedINPChart />
15 changes: 0 additions & 15 deletions packages/docs/src/components/ServerSideRenderedINPChart.astro

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const columns = [
},
{ key: 'firstPaintMs', header: 'First Paint' },
{ key: 'fcpMs', header: 'FCP' },
{ key: 'inpMs', header: 'INP' },
]

const tableData = serverSideRenderedStats
Expand Down
24 changes: 22 additions & 2 deletions packages/docs/src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,35 @@ const runtimeSchema = z.object({
.object({
firstPaintMs: z.number(),
fcpMs: z.number(),
inpMs: z.number(),
inpMs: z.number().optional(),
interactionTests: z
.object({
scenario: z.literal('first-row-detail-navigation'),
source: z.literal('lighthouse-inp-breakdown'),
interactionLatencyMs: z.number().positive(),
inputDelayMs: z.number().nonnegative(),
processingDurationMs: z.number().nonnegative(),
presentationDelayMs: z.number().nonnegative(),
})
.optional(),
runs: z.number(),
})
.optional(),
serverSideRenderedTests: z
.object({
firstPaintMs: z.number(),
fcpMs: z.number(),
inpMs: z.number(),
inpMs: z.number().optional(),
interactionTests: z
.object({
scenario: z.literal('first-row-detail-navigation'),
source: z.literal('lighthouse-inp-breakdown'),
interactionLatencyMs: z.number().positive(),
inputDelayMs: z.number().nonnegative(),
processingDurationMs: z.number().nonnegative(),
presentationDelayMs: z.number().nonnegative(),
})
.optional(),
runs: z.number(),
})
.optional(),
Expand Down
23 changes: 17 additions & 6 deletions packages/docs/src/content/docs/methodology.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,16 @@ throughput, and load behavior for comparable production apps.
- Metrics are measured with Lighthouse flow in Chromium through Puppeteer.
- First Paint and First Contentful Paint are measured on initial navigation to
`/client-side-rendered`.
- Interaction to Next Paint is measured by clicking the first row's detail link
and waiting for the detail view.
- Benchmarks run 5 times by default and average successful metrics.
- A controlled interaction clicks the first row's detail link and waits for the
detail view. Lighthouse's
[INP breakdown insight](https://developer.chrome.com/docs/performance/insights/inp-breakdown)
processes Chrome Event Timing trace data and reports its input delay,
processing duration, and presentation delay. Their sum is recorded as the
interaction latency. This is one controlled interaction, not Google's
page-lifetime
[Interaction to Next Paint (INP)](https://web.dev/articles/inp) metric.
- Benchmarks run 5 times by default. Paint and interaction timing values are
averaged across those runs.
- These are route-based client-side rendering tests, not full-app SPA mode
tests. Each app uses its normal production build and configures the benchmark
routes so the measured table and detail content are rendered in the browser.
Expand Down Expand Up @@ -202,14 +209,18 @@ throughput, and load behavior for comparable production apps.
- Metrics are measured with Lighthouse flow in Chromium through Puppeteer.
- First Paint and First Contentful Paint are measured on initial navigation to
`/server-side-rendered`.
- Interaction to Next Paint is measured by clicking the first row's detail link
and waiting for `/server-side-rendered/:id`.
- A controlled interaction clicks the first row's detail link and waits for
`/server-side-rendered/:id`. Lighthouse's INP breakdown insight processes
Chrome Event Timing trace data and reports its input delay, processing
duration, and presentation delay. Their sum is recorded as the interaction
latency.
- Detail links use each framework's default production navigation component or
idiomatic anchor behavior. Default meta-framework route prefetching or
preloading is allowed when it is part of the framework's default link
behavior, but the measured SSR routes are still rendered on demand rather than
converted to prerendered static output.
- Benchmarks run 5 times by default and average successful metrics.
- Benchmarks run 5 times by default and require every Chrome paint and
interaction measurement to be present and greater than zero.
- Astro keeps the default static output mode, but the measured
`/server-side-rendered` route and its detail route use
`export const prerender = false` so they are rendered on demand by the
Expand Down
30 changes: 0 additions & 30 deletions packages/docs/src/lib/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ export const serverSideRenderedStats = runtimeEntries
isFocused: framework.isFocused,
firstPaintMs: `${framework.serverSideRenderedTests!.firstPaintMs}ms`,
fcpMs: `${framework.serverSideRenderedTests!.fcpMs}ms`,
inpMs: `${framework.serverSideRenderedTests!.inpMs}ms`,
}))

export const clientSideRenderedStats = runtimeEntries
Expand All @@ -179,7 +178,6 @@ export const clientSideRenderedStats = runtimeEntries
isFocused: framework.isFocused,
firstPaintMs: `${framework.clientSideRenderedTests!.firstPaintMs}ms`,
fcpMs: `${framework.clientSideRenderedTests!.fcpMs}ms`,
inpMs: `${framework.clientSideRenderedTests!.inpMs}ms`,
}))

export const depsStats = starterStats.map((f) => ({
Expand Down Expand Up @@ -324,20 +322,6 @@ export const chartServerSideRenderedFCPData = runtimeEntries
focused: f.isFocused,
}))

export const chartServerSideRenderedINPData = runtimeEntries
.map((entry) => entry.data)
.sort((a, b) => a.order - b.order)
.filter(
(f) =>
f.serverSideRenderedTests != null &&
Number.isFinite(f.serverSideRenderedTests.inpMs),
)
.map((f) => ({
name: f.name,
value: f.serverSideRenderedTests!.inpMs,
focused: f.isFocused,
}))

export const chartClientSideRenderedFPData = runtimeEntries
.map((entry) => entry.data)
.sort((a, b) => a.order - b.order)
Expand Down Expand Up @@ -366,20 +350,6 @@ export const chartClientSideRenderedFCPData = runtimeEntries
focused: f.isFocused,
}))

export const chartClientSideRenderedINPData = runtimeEntries
.map((entry) => entry.data)
.sort((a, b) => a.order - b.order)
.filter(
(f) =>
f.clientSideRenderedTests != null &&
Number.isFinite(f.clientSideRenderedTests.inpMs),
)
.map((f) => ({
name: f.name,
value: f.clientSideRenderedTests!.inpMs,
focused: f.isFocused,
}))

export const coreJsTableData = starterStats.map((f) => {
const hasCorejs = (f.vendoredCoreJsUnnecessaryModules?.length ?? 0) > 0
return {
Expand Down
9 changes: 9 additions & 0 deletions packages/stats-generator/src/clientSideRendered/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface ClientSideRenderedFrameworkConfig {
package: string
/** Filename of the serve script in src/serve/ */
serveScript: string
fullDocumentNavigation: boolean
}

const CLIENT_SIDE_RENDERED_FRAMEWORKS: ClientSideRenderedFrameworkConfig[] = [
Expand All @@ -23,42 +24,49 @@ const CLIENT_SIDE_RENDERED_FRAMEWORKS: ClientSideRenderedFrameworkConfig[] = [
displayName: 'Astro Client Side Rendered',
package: 'app-astro',
serveScript: 'astro.ts',
fullDocumentNavigation: true,
},
{
name: 'next-client-side-rendered',
displayName: 'Next.js Client Side Rendered',
package: 'app-next-js',
serveScript: 'next.ts',
fullDocumentNavigation: false,
},
{
name: 'nuxt-client-side-rendered',
displayName: 'Nuxt Client Side Rendered',
package: 'app-nuxt',
serveScript: 'nitro.ts',
fullDocumentNavigation: false,
},
{
name: 'react-router-client-side-rendered',
displayName: 'React Router Client Side Rendered',
package: 'app-react-router',
serveScript: 'react-router.ts',
fullDocumentNavigation: false,
},
{
name: 'solid-start-client-side-rendered',
displayName: 'SolidStart Client Side Rendered',
package: 'app-solid-start',
serveScript: 'nitro.ts',
fullDocumentNavigation: false,
},
{
name: 'sveltekit-client-side-rendered',
displayName: 'SvelteKit Client Side Rendered',
package: 'app-sveltekit',
serveScript: 'sveltekit.ts',
fullDocumentNavigation: false,
},
{
name: 'tanstack-start-client-side-rendered',
displayName: 'TanStack Start Client Side Rendered',
package: 'app-tanstack-start-react',
serveScript: 'tanstack-start.ts',
fullDocumentNavigation: false,
},
]

Expand Down Expand Up @@ -155,6 +163,7 @@ export async function runClientSideRenderedBenchmark(
config.name,
config.displayName,
runs,
config.fullDocumentNavigation,
)
} finally {
killServer()
Expand Down
Loading
Loading