From a34aa907ed979148e03f799ea91f7801d9249a65 Mon Sep 17 00:00:00 2001 From: Alexander Karan Date: Wed, 22 Apr 2026 11:46:36 +0800 Subject: [PATCH] Docs for MPA tests --- packages/docs/src/components/MPACharts.astro | 18 +++++++++ .../docs/src/components/MPAFCPChart.astro | 10 +++++ packages/docs/src/components/MPAFPChart.astro | 10 +++++ .../docs/src/components/MPAINPChart.astro | 10 +++++ .../components/MPAStatsMethodologyNotes.astro | 28 ++++++++++++++ .../docs/src/components/MPAStatsTable.astro | 33 +++++++++++++++++ packages/docs/src/content.config.ts | 5 +++ .../docs/src/content/runtime/app-astro.json | 10 +++-- .../docs/src/content/runtime/app-next-js.json | 16 +++++--- .../docs/src/content/runtime/app-nuxt.json | 16 +++++--- .../src/content/runtime/app-react-router.json | 16 +++++--- .../src/content/runtime/app-solid-start.json | 16 +++++--- .../src/content/runtime/app-sveltekit.json | 16 +++++--- .../runtime/app-tanstack-start-react.json | 16 +++++--- packages/docs/src/lib/collections.ts | 37 ++++++++++++++++++- .../docs/src/pages/framework/[slug].astro | 30 +++++++++++++++ packages/docs/src/pages/index.astro | 6 ++- packages/docs/src/pages/run-time.astro | 7 ++++ 18 files changed, 258 insertions(+), 42 deletions(-) create mode 100644 packages/docs/src/components/MPACharts.astro create mode 100644 packages/docs/src/components/MPAFCPChart.astro create mode 100644 packages/docs/src/components/MPAFPChart.astro create mode 100644 packages/docs/src/components/MPAINPChart.astro create mode 100644 packages/docs/src/components/MPAStatsMethodologyNotes.astro create mode 100644 packages/docs/src/components/MPAStatsTable.astro diff --git a/packages/docs/src/components/MPACharts.astro b/packages/docs/src/components/MPACharts.astro new file mode 100644 index 00000000..fd43db18 --- /dev/null +++ b/packages/docs/src/components/MPACharts.astro @@ -0,0 +1,18 @@ +--- +import ChartTabs from './ChartTabs.astro' +import MPAFPChart from './MPAFPChart.astro' +import MPAFCPChart from './MPAFCPChart.astro' +import MPAINPChart from './MPAINPChart.astro' +--- + + + + + + diff --git a/packages/docs/src/components/MPAFCPChart.astro b/packages/docs/src/components/MPAFCPChart.astro new file mode 100644 index 00000000..b0fd7d98 --- /dev/null +++ b/packages/docs/src/components/MPAFCPChart.astro @@ -0,0 +1,10 @@ +--- +import { chartMPAFCPData } from '../lib/collections' +import ComparisonBarChart from './ComparisonBarChart.astro' +--- + + diff --git a/packages/docs/src/components/MPAFPChart.astro b/packages/docs/src/components/MPAFPChart.astro new file mode 100644 index 00000000..2d0861f9 --- /dev/null +++ b/packages/docs/src/components/MPAFPChart.astro @@ -0,0 +1,10 @@ +--- +import { chartMPAFPData } from '../lib/collections' +import ComparisonBarChart from './ComparisonBarChart.astro' +--- + + diff --git a/packages/docs/src/components/MPAINPChart.astro b/packages/docs/src/components/MPAINPChart.astro new file mode 100644 index 00000000..f3f23c2b --- /dev/null +++ b/packages/docs/src/components/MPAINPChart.astro @@ -0,0 +1,10 @@ +--- +import { chartMPAINPData } from '../lib/collections' +import ComparisonBarChart from './ComparisonBarChart.astro' +--- + + diff --git a/packages/docs/src/components/MPAStatsMethodologyNotes.astro b/packages/docs/src/components/MPAStatsMethodologyNotes.astro new file mode 100644 index 00000000..4eaccdaf --- /dev/null +++ b/packages/docs/src/components/MPAStatsMethodologyNotes.astro @@ -0,0 +1,28 @@ +--- +import MethodologyNotes from '../components/MethodologyNotes.astro' +--- + + +
  • 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 dynamic import with ssr: false to prevent build-time prerendering. TanStack Start uses its built-in spa mode. + React Router disables SSR entirely via ssr: false in its config. + All other frameworks (Nuxt, SvelteKit, SolidStart, Astro) disable SSR per-route + without a separate build. +
  • +
    diff --git a/packages/docs/src/components/MPAStatsTable.astro b/packages/docs/src/components/MPAStatsTable.astro new file mode 100644 index 00000000..2f4e9f5d --- /dev/null +++ b/packages/docs/src/components/MPAStatsTable.astro @@ -0,0 +1,33 @@ +--- +import { mpaStats } from '../lib/collections' +import { getFrameworkSlug } from '../lib/utils' +import MethodologyTag from './MethodologyTag.astro' +import StatsTable from './StatsTable.astro' + +const columns = [ + { + key: 'name', + header: 'Framework', + nameCell: true, + href: (row: Record) => + row.package !== 'app-baseline-html' + ? `/framework/${getFrameworkSlug(row.package as string)}` + : null, + }, + { key: 'mpaFirstPaintMs', header: 'First Paint' }, + { key: 'mpaFCPMs', header: 'FCP' }, + { key: 'mpaINPMs', header: 'INP' }, +] + +const tableData = mpaStats +--- + + + Measured on GitHub Actions (ubuntu-latest, Node 24) using Lighthouse flow with + Chromium. + + diff --git a/packages/docs/src/content.config.ts b/packages/docs/src/content.config.ts index ec52200c..55a5f22c 100644 --- a/packages/docs/src/content.config.ts +++ b/packages/docs/src/content.config.ts @@ -61,6 +61,11 @@ const runtimeCollection = defineCollection({ spaFCPMs: z.number().optional(), spaINPMs: z.number().optional(), spaRuns: z.number().optional(), + // MPA paint + interaction metrics + mpaFirstPaintMs: z.number().optional(), + mpaFCPMs: z.number().optional(), + mpaINPMs: z.number().optional(), + mpaRuns: z.number().optional(), }), }) diff --git a/packages/docs/src/content/runtime/app-astro.json b/packages/docs/src/content/runtime/app-astro.json index d2c3aede..302c5cb1 100644 --- a/packages/docs/src/content/runtime/app-astro.json +++ b/packages/docs/src/content/runtime/app-astro.json @@ -8,12 +8,16 @@ "ssrSamples": 3656, "ssrBodySizeKb": 99.86, "ssrDuplicationFactor": 1, - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:06:35.326Z", + "runner": "local", "frameworkVersion": "5.16.15", "order": 1, "spaFirstPaintMs": 36.8, "spaFCPMs": 36.8, "spaINPMs": 98.72, - "spaRuns": 5 + "spaRuns": 5, + "mpaFirstPaintMs": 90.33, + "mpaFCPMs": 90.39, + "mpaINPMs": 22.56, + "mpaRuns": 3 } diff --git a/packages/docs/src/content/runtime/app-next-js.json b/packages/docs/src/content/runtime/app-next-js.json index 72d94746..4ad8f8b1 100644 --- a/packages/docs/src/content/runtime/app-next-js.json +++ b/packages/docs/src/content/runtime/app-next-js.json @@ -3,8 +3,8 @@ "package": "app-next-js", "isFocused": true, "type": "ssr-app", - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:07:39.298Z", + "runner": "local", "frameworkVersion": "16.1.1", "ssrOpsPerSec": 129, "ssrAvgLatencyMs": 7.74, @@ -12,8 +12,12 @@ "ssrBodySizeKb": 198.59, "ssrDuplicationFactor": 2, "order": 3, - "spaFirstPaintMs": 48, - "spaFCPMs": 48, - "spaINPMs": 75.48, - "spaRuns": 5 + "spaFirstPaintMs": 371, + "spaFCPMs": 370.94, + "spaINPMs": 23.38, + "spaRuns": 1, + "mpaFirstPaintMs": 127.33, + "mpaFCPMs": 127.19, + "mpaINPMs": 20.21, + "mpaRuns": 3 } diff --git a/packages/docs/src/content/runtime/app-nuxt.json b/packages/docs/src/content/runtime/app-nuxt.json index d1307003..060d5190 100644 --- a/packages/docs/src/content/runtime/app-nuxt.json +++ b/packages/docs/src/content/runtime/app-nuxt.json @@ -8,12 +8,16 @@ "ssrSamples": 2478, "ssrBodySizeKb": 201.18, "ssrDuplicationFactor": 2, - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:26:28.141Z", + "runner": "local", "frameworkVersion": "4.2.2", "order": 4, - "spaFirstPaintMs": 32.8, - "spaFCPMs": 32.8, - "spaINPMs": 69.98, - "spaRuns": 5 + "mpaFirstPaintMs": 89.67, + "mpaFCPMs": 89.6, + "mpaINPMs": 24.05, + "mpaRuns": 3, + "spaFirstPaintMs": 111.67, + "spaFCPMs": 111.7, + "spaINPMs": 22.65, + "spaRuns": 3 } diff --git a/packages/docs/src/content/runtime/app-react-router.json b/packages/docs/src/content/runtime/app-react-router.json index 4deadbab..64f71996 100644 --- a/packages/docs/src/content/runtime/app-react-router.json +++ b/packages/docs/src/content/runtime/app-react-router.json @@ -3,8 +3,8 @@ "package": "app-react-router", "isFocused": true, "type": "ssr-app", - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:29:46.068Z", + "runner": "local", "frameworkVersion": "7.10.1", "ssrOpsPerSec": 64, "ssrAvgLatencyMs": 15.528, @@ -12,8 +12,12 @@ "ssrBodySizeKb": 211.14, "ssrDuplicationFactor": 2, "order": 5, - "spaFirstPaintMs": 35.2, - "spaFCPMs": 35.2, - "spaINPMs": 59.78, - "spaRuns": 5 + "mpaFirstPaintMs": 167.33, + "mpaFCPMs": 167.24, + "mpaINPMs": 24.37, + "mpaRuns": 3, + "spaFirstPaintMs": 121, + "spaFCPMs": 121.16, + "spaINPMs": 22.24, + "spaRuns": 3 } diff --git a/packages/docs/src/content/runtime/app-solid-start.json b/packages/docs/src/content/runtime/app-solid-start.json index eeaa4f29..da53aae5 100644 --- a/packages/docs/src/content/runtime/app-solid-start.json +++ b/packages/docs/src/content/runtime/app-solid-start.json @@ -3,8 +3,8 @@ "package": "app-solid-start", "isFocused": true, "type": "ssr-app", - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:30:50.803Z", + "runner": "local", "frameworkVersion": "1.2.1", "ssrOpsPerSec": 234, "ssrAvgLatencyMs": 4.275, @@ -12,8 +12,12 @@ "ssrBodySizeKb": 225.49, "ssrDuplicationFactor": 2, "order": 6, - "spaFirstPaintMs": 22.4, - "spaFCPMs": 22.4, - "spaINPMs": 63.09, - "spaRuns": 5 + "mpaFirstPaintMs": 106.33, + "mpaFCPMs": 106.31, + "mpaINPMs": 23.79, + "mpaRuns": 3, + "spaFirstPaintMs": 114, + "spaFCPMs": 114.32, + "spaINPMs": 21.33, + "spaRuns": 3 } diff --git a/packages/docs/src/content/runtime/app-sveltekit.json b/packages/docs/src/content/runtime/app-sveltekit.json index bd164f45..1bdd079e 100644 --- a/packages/docs/src/content/runtime/app-sveltekit.json +++ b/packages/docs/src/content/runtime/app-sveltekit.json @@ -3,8 +3,8 @@ "package": "app-sveltekit", "isFocused": true, "type": "ssr-app", - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:11:57.395Z", + "runner": "local", "frameworkVersion": "2.49.4", "ssrOpsPerSec": 259, "ssrAvgLatencyMs": 3.858, @@ -12,8 +12,12 @@ "ssrBodySizeKb": 183.55, "ssrDuplicationFactor": 2, "order": 7, - "spaFirstPaintMs": 26.4, - "spaFCPMs": 26.4, - "spaINPMs": 64.89, - "spaRuns": 5 + "spaFirstPaintMs": 93, + "spaFCPMs": 93.14, + "spaINPMs": 20.37, + "spaRuns": 1, + "mpaFirstPaintMs": 109, + "mpaFCPMs": 108.78, + "mpaINPMs": 21.66, + "mpaRuns": 3 } diff --git a/packages/docs/src/content/runtime/app-tanstack-start-react.json b/packages/docs/src/content/runtime/app-tanstack-start-react.json index a5ac7f38..6cc6e45c 100644 --- a/packages/docs/src/content/runtime/app-tanstack-start-react.json +++ b/packages/docs/src/content/runtime/app-tanstack-start-react.json @@ -3,8 +3,8 @@ "package": "app-tanstack-start-react", "isFocused": true, "type": "ssr-app", - "timingMeasuredAt": "2026-03-08T00:31:57.171Z", - "runner": "ubuntu-latest", + "timingMeasuredAt": "2026-04-21T05:31:57.912Z", + "runner": "local", "frameworkVersion": "1.145.3", "ssrOpsPerSec": 185, "ssrAvgLatencyMs": 5.395, @@ -12,8 +12,12 @@ "ssrBodySizeKb": 193.53, "ssrDuplicationFactor": 2, "order": 8, - "spaFirstPaintMs": 40.8, - "spaFCPMs": 40.8, - "spaINPMs": 59.27, - "spaRuns": 5 + "mpaFirstPaintMs": 109.33, + "mpaFCPMs": 109.36, + "mpaINPMs": 23.98, + "mpaRuns": 3, + "spaFirstPaintMs": 693, + "spaFCPMs": 693.07, + "spaINPMs": 104.6, + "spaRuns": 3 } diff --git a/packages/docs/src/lib/collections.ts b/packages/docs/src/lib/collections.ts index ad0cec22..ec51d68d 100644 --- a/packages/docs/src/lib/collections.ts +++ b/packages/docs/src/lib/collections.ts @@ -12,6 +12,19 @@ const ssrStats = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) +const mpaStats = runtimeEntries + .map((entry) => entry.data) + .sort((a, b) => a.order - b.order) + .filter((f) => f?.name != null && Number.isFinite(f.mpaFirstPaintMs)) + .map((f) => ({ + name: f.name, + package: f.package, + isFocused: f.isFocused, + mpaFirstPaintMs: `${f.mpaFirstPaintMs}ms`, + mpaFCPMs: `${f.mpaFCPMs}ms`, + mpaINPMs: `${f.mpaINPMs}ms`, + })) + const spaStats = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) @@ -63,6 +76,28 @@ export const chartDuplicateDependencyData = starterStats focused: f.isFocused, })) +export const chartMPAFPData = runtimeEntries + .map((entry) => entry.data) + .sort((a, b) => a.order - b.order) + .filter((f) => f?.name != null && Number.isFinite(f.mpaFirstPaintMs)) + .map((f) => ({ + name: f.name, + value: f.mpaFirstPaintMs!, + focused: f.isFocused, + })) + +export const chartMPAFCPData = runtimeEntries + .map((entry) => entry.data) + .sort((a, b) => a.order - b.order) + .filter((f) => f?.name != null && Number.isFinite(f.mpaFCPMs)) + .map((f) => ({ name: f.name, value: f.mpaFCPMs!, focused: f.isFocused })) + +export const chartMPAINPData = runtimeEntries + .map((entry) => entry.data) + .sort((a, b) => a.order - b.order) + .filter((f) => f?.name != null && Number.isFinite(f.mpaINPMs)) + .map((f) => ({ name: f.name, value: f.mpaINPMs!, focused: f.isFocused })) + export const chartSPAFPData = runtimeEntries .map((entry) => entry.data) .sort((a, b) => a.order - b.order) @@ -100,4 +135,4 @@ export const coreJsTableData = starterStats.map((f) => { } }) -export { ssrStats, spaStats, depsStats, buildInstallData } +export { ssrStats, spaStats, mpaStats, depsStats, buildInstallData } diff --git a/packages/docs/src/pages/framework/[slug].astro b/packages/docs/src/pages/framework/[slug].astro index 0c8f23b0..31424cbc 100644 --- a/packages/docs/src/pages/framework/[slug].astro +++ b/packages/docs/src/pages/framework/[slug].astro @@ -3,6 +3,7 @@ import { getCollection } from 'astro:content' import BackLink from '../../components/BackLink.astro' import DevTimeChart from '../../components/DevTimeChart.astro' import MethodologyTag from '../../components/MethodologyTag.astro' +import MPAStatsMethodologyNotes from '../../components/MPAStatsMethodologyNotes.astro' import PageHeader from '../../components/PageHeader.astro' import SPAStatsMethodologyNotes from '../../components/SPAStatsMethodologyNotes.astro' import SSRStatsMethodologyNotes from '../../components/SSRStatsMethodologyNotes.astro' @@ -183,6 +184,24 @@ const spaData = runtime }, ] : [] + +const mpaColumns = [ + { key: 'name', header: 'Framework', nameCell: true }, + { key: 'mpaFirstPaintMs', header: 'First Paint' }, + { key: 'mpaFCPMs', header: 'FCP' }, + { key: 'mpaINPMs', header: 'INP' }, +] + +const mpaData = runtime + ? [ + { + name: runtime.name, + mpaFirstPaintMs: `${runtime.mpaFirstPaintMs}ms`, + mpaFCPMs: `${runtime.mpaFCPMs}ms`, + mpaINPMs: `${runtime.mpaINPMs}ms`, + }, + ] + : [] --- @@ -333,6 +352,17 @@ const spaData = runtime data={spaData} /> +

    MPA Performance

    + + Measured on GitHub Actions (ubuntu-latest, Node 24) using Lighthouse + flow with Chromium. + + + ) } diff --git a/packages/docs/src/pages/index.astro b/packages/docs/src/pages/index.astro index 426b5425..d146fd0a 100644 --- a/packages/docs/src/pages/index.astro +++ b/packages/docs/src/pages/index.astro @@ -2,15 +2,14 @@ import BuildSizeCharts from '../components/BuildSizeCharts.astro' import CoreJsTable from '../components/CoreJsTable.astro' import DependencyCharts from '../components/DependencyCharts.astro' +import MPACharts from '../components/MPACharts.astro' import NodeModulesSizeCharts from '../components/NodeModulesSizeCharts.astro' import Description from '../components/Description.astro' import DetailsLink from '../components/DetailsLink.astro' import FocusedToggle from '../components/FocusedToggle.astro' import PageHeader from '../components/PageHeader.astro' import SPACharts from '../components/SPACharts.astro' -import SPAStatsTable from '../components/SPAStatsTable.astro' import SSRCharts from '../components/SSRCharts.astro' -import SSRStatsTable from '../components/SSRStatsTable.astro' import Layout from '../layouts/Layout.astro' --- @@ -64,6 +63,9 @@ import Layout from '../layouts/Layout.astro'

    SPA Performance

    +

    MPA Performance

    + +