From 1c32fba7e4234eef12b65bb67aa7a00b89c8a11d Mon Sep 17 00:00:00 2001 From: Alexander Karan Date: Mon, 20 Apr 2026 11:52:27 +0800 Subject: [PATCH 1/3] First pass at setting up MPA routes for the test --- packages/app-astro/src/pages/mpa.astro | 31 ++++++++++++++ packages/app-astro/src/pages/mpa/detail.astro | 14 +++++++ packages/app-next-js/app/mpa/detail/page.tsx | 9 ++++ packages/app-next-js/app/mpa/page.tsx | 23 ++++++++++ packages/app-nuxt/app/pages/mpa/detail.vue | 8 ++++ packages/app-nuxt/app/pages/mpa/index.vue | 19 +++++++++ packages/app-react-router/app/routes.ts | 2 + .../app/routes/mpa.detail.tsx | 11 +++++ packages/app-react-router/app/routes/mpa.tsx | 25 +++++++++++ .../app-solid-start/src/routes/mpa/detail.tsx | 7 ++++ .../app-solid-start/src/routes/mpa/index.tsx | 34 +++++++++++++++ .../src/routes/mpa/+page.server.ts | 5 +++ .../app-sveltekit/src/routes/mpa/+page.svelte | 17 ++++++++ .../src/routes/mpa/detail/+page.server.ts | 6 +++ .../src/routes/mpa/detail/+page.svelte | 5 +++ .../src/routeTree.gen.ts | 42 +++++++++++++++++-- .../src/routes/mpa.tsx | 27 ++++++++++++ .../src/routes/mpa_.detail.tsx | 15 +++++++ 18 files changed, 297 insertions(+), 3 deletions(-) create mode 100644 packages/app-astro/src/pages/mpa.astro create mode 100644 packages/app-astro/src/pages/mpa/detail.astro create mode 100644 packages/app-next-js/app/mpa/detail/page.tsx create mode 100644 packages/app-next-js/app/mpa/page.tsx create mode 100644 packages/app-nuxt/app/pages/mpa/detail.vue create mode 100644 packages/app-nuxt/app/pages/mpa/index.vue create mode 100644 packages/app-react-router/app/routes/mpa.detail.tsx create mode 100644 packages/app-react-router/app/routes/mpa.tsx create mode 100644 packages/app-solid-start/src/routes/mpa/detail.tsx create mode 100644 packages/app-solid-start/src/routes/mpa/index.tsx create mode 100644 packages/app-sveltekit/src/routes/mpa/+page.server.ts create mode 100644 packages/app-sveltekit/src/routes/mpa/+page.svelte create mode 100644 packages/app-sveltekit/src/routes/mpa/detail/+page.server.ts create mode 100644 packages/app-sveltekit/src/routes/mpa/detail/+page.svelte create mode 100644 packages/app-tanstack-start-react/src/routes/mpa.tsx create mode 100644 packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx diff --git a/packages/app-astro/src/pages/mpa.astro b/packages/app-astro/src/pages/mpa.astro new file mode 100644 index 00000000..12b05326 --- /dev/null +++ b/packages/app-astro/src/pages/mpa.astro @@ -0,0 +1,31 @@ +--- +const entries = Array.from({ length: 1000 }, () => ({ + id: crypto.randomUUID(), + name: crypto.randomUUID(), +})) +--- + + + + + + Astro MPA Benchmark + + + + + { + entries.map((entry) => ( + + + + + + )) + } + +
{entry.id}{entry.name} + View → +
+ + diff --git a/packages/app-astro/src/pages/mpa/detail.astro b/packages/app-astro/src/pages/mpa/detail.astro new file mode 100644 index 00000000..8a641604 --- /dev/null +++ b/packages/app-astro/src/pages/mpa/detail.astro @@ -0,0 +1,14 @@ +--- +const id = Astro.url.searchParams.get('id') +--- + + + + + + Astro MPA Detail + + +

{id}

+ + diff --git a/packages/app-next-js/app/mpa/detail/page.tsx b/packages/app-next-js/app/mpa/detail/page.tsx new file mode 100644 index 00000000..3d6b31e7 --- /dev/null +++ b/packages/app-next-js/app/mpa/detail/page.tsx @@ -0,0 +1,9 @@ +interface Props { + searchParams: Promise<{ id?: string }> +} + +export default async function MpaDetailPage({ searchParams }: Props) { + const { id } = await searchParams + + return

{id}

+} diff --git a/packages/app-next-js/app/mpa/page.tsx b/packages/app-next-js/app/mpa/page.tsx new file mode 100644 index 00000000..2f38d6ab --- /dev/null +++ b/packages/app-next-js/app/mpa/page.tsx @@ -0,0 +1,23 @@ +import { testData } from '../../../testdata/src/ssr' + +export const dynamic = 'force-dynamic' + +export default async function MpaPage() { + const entries = await testData() + + return ( + + + {entries.map((entry) => ( + + + + + + ))} + +
{entry.id}{entry.name} + View → +
+ ) +} diff --git a/packages/app-nuxt/app/pages/mpa/detail.vue b/packages/app-nuxt/app/pages/mpa/detail.vue new file mode 100644 index 00000000..c68de24a --- /dev/null +++ b/packages/app-nuxt/app/pages/mpa/detail.vue @@ -0,0 +1,8 @@ + + + diff --git a/packages/app-nuxt/app/pages/mpa/index.vue b/packages/app-nuxt/app/pages/mpa/index.vue new file mode 100644 index 00000000..a5b946ae --- /dev/null +++ b/packages/app-nuxt/app/pages/mpa/index.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/app-react-router/app/routes.ts b/packages/app-react-router/app/routes.ts index 92b333be..1c1d80fa 100644 --- a/packages/app-react-router/app/routes.ts +++ b/packages/app-react-router/app/routes.ts @@ -6,4 +6,6 @@ export default [ ...(isSpa ? [] : [index('routes/home.tsx')]), route('/spa', 'routes/spa.tsx'), route('/spa/detail', 'routes/spa.detail.tsx'), + route('/mpa', 'routes/mpa.tsx'), + route('/mpa/detail', 'routes/mpa.detail.tsx'), ] satisfies RouteConfig diff --git a/packages/app-react-router/app/routes/mpa.detail.tsx b/packages/app-react-router/app/routes/mpa.detail.tsx new file mode 100644 index 00000000..3af22cf8 --- /dev/null +++ b/packages/app-react-router/app/routes/mpa.detail.tsx @@ -0,0 +1,11 @@ +import type { Route } from './+types/mpa.detail' + +export async function loader({ request }: Route.LoaderArgs) { + const url = new URL(request.url) + const id = url.searchParams.get('id') + return { id } +} + +export default function MpaDetailPage({ loaderData }: Route.ComponentProps) { + return

{loaderData.id}

+} diff --git a/packages/app-react-router/app/routes/mpa.tsx b/packages/app-react-router/app/routes/mpa.tsx new file mode 100644 index 00000000..bed4d090 --- /dev/null +++ b/packages/app-react-router/app/routes/mpa.tsx @@ -0,0 +1,25 @@ +import { testData } from '../../../testdata/src/ssr' +import type { Route } from './+types/mpa' + +export async function loader() { + const data = await testData() + return { data } +} + +export default function MpaPage({ loaderData }: Route.ComponentProps) { + return ( + + + {loaderData.data.map((entry) => ( + + + + + + ))} + +
{entry.id}{entry.name} + View → +
+ ) +} diff --git a/packages/app-solid-start/src/routes/mpa/detail.tsx b/packages/app-solid-start/src/routes/mpa/detail.tsx new file mode 100644 index 00000000..0f92099b --- /dev/null +++ b/packages/app-solid-start/src/routes/mpa/detail.tsx @@ -0,0 +1,7 @@ +import { useSearchParams } from '@solidjs/router' + +export default function MpaDetailPage() { + const [searchParams] = useSearchParams() + + return

{searchParams.id}

+} diff --git a/packages/app-solid-start/src/routes/mpa/index.tsx b/packages/app-solid-start/src/routes/mpa/index.tsx new file mode 100644 index 00000000..05e39bec --- /dev/null +++ b/packages/app-solid-start/src/routes/mpa/index.tsx @@ -0,0 +1,34 @@ +import { For } from 'solid-js' +import { query, createAsync } from '@solidjs/router' +import { testData } from '../../../../testdata/src/ssr' + +const getData = query(async () => { + 'use server' + return await testData() +}, 'mpa-data') + +export const route = { + load: () => getData(), +} + +export default function MpaPage() { + const data = createAsync(() => getData()) + + return ( + + + + {(entry) => ( + + + + + + )} + + +
{entry.id}{entry.name} + View → +
+ ) +} diff --git a/packages/app-sveltekit/src/routes/mpa/+page.server.ts b/packages/app-sveltekit/src/routes/mpa/+page.server.ts new file mode 100644 index 00000000..65e279d8 --- /dev/null +++ b/packages/app-sveltekit/src/routes/mpa/+page.server.ts @@ -0,0 +1,5 @@ +import { testData } from '../../../../testdata/src/ssr' + +export const load = async () => { + return { entries: await testData() } +} diff --git a/packages/app-sveltekit/src/routes/mpa/+page.svelte b/packages/app-sveltekit/src/routes/mpa/+page.svelte new file mode 100644 index 00000000..bb7239e0 --- /dev/null +++ b/packages/app-sveltekit/src/routes/mpa/+page.svelte @@ -0,0 +1,17 @@ + + + + + {#each data.entries as entry (entry.id)} + + + + + + {/each} + +
{entry.id}{entry.name} + View → +
diff --git a/packages/app-sveltekit/src/routes/mpa/detail/+page.server.ts b/packages/app-sveltekit/src/routes/mpa/detail/+page.server.ts new file mode 100644 index 00000000..e38fc13c --- /dev/null +++ b/packages/app-sveltekit/src/routes/mpa/detail/+page.server.ts @@ -0,0 +1,6 @@ +import type { PageServerLoad } from './$types' + +export const load: PageServerLoad = ({ url }) => { + const id = url.searchParams.get('id') + return { id } +} diff --git a/packages/app-sveltekit/src/routes/mpa/detail/+page.svelte b/packages/app-sveltekit/src/routes/mpa/detail/+page.svelte new file mode 100644 index 00000000..f446fc16 --- /dev/null +++ b/packages/app-sveltekit/src/routes/mpa/detail/+page.svelte @@ -0,0 +1,5 @@ + + +

{data.id}

diff --git a/packages/app-tanstack-start-react/src/routeTree.gen.ts b/packages/app-tanstack-start-react/src/routeTree.gen.ts index 46adef8f..cdbd5daa 100644 --- a/packages/app-tanstack-start-react/src/routeTree.gen.ts +++ b/packages/app-tanstack-start-react/src/routeTree.gen.ts @@ -9,10 +9,17 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as MpaRouteImport } from './routes/mpa' import { Route as SpaRouteImport } from './routes/spa' import { Route as IndexRouteImport } from './routes/index' +import { Route as MpaDetailRouteImport } from './routes/mpa_.detail' import { Route as SpaDetailRouteImport } from './routes/spa_.detail' +const MpaRoute = MpaRouteImport.update({ + id: '/mpa', + path: '/mpa', + getParentRoute: () => rootRouteImport, +} as any) const SpaRoute = SpaRouteImport.update({ id: '/spa', path: '/spa', @@ -23,6 +30,11 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) +const MpaDetailRoute = MpaDetailRouteImport.update({ + id: '/mpa_/detail', + path: '/mpa/detail', + getParentRoute: () => rootRouteImport, +} as any) const SpaDetailRoute = SpaDetailRouteImport.update({ id: '/spa_/detail', path: '/spa/detail', @@ -31,36 +43,51 @@ const SpaDetailRoute = SpaDetailRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/mpa': typeof MpaRoute '/spa': typeof SpaRoute + '/mpa/detail': typeof MpaDetailRoute '/spa/detail': typeof SpaDetailRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/mpa': typeof MpaRoute '/spa': typeof SpaRoute + '/mpa/detail': typeof MpaDetailRoute '/spa/detail': typeof SpaDetailRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/mpa': typeof MpaRoute '/spa': typeof SpaRoute + '/mpa_/detail': typeof MpaDetailRoute '/spa_/detail': typeof SpaDetailRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/spa' | '/spa/detail' + fullPaths: '/' | '/mpa' | '/spa' | '/mpa/detail' | '/spa/detail' fileRoutesByTo: FileRoutesByTo - to: '/' | '/spa' | '/spa/detail' - id: '__root__' | '/' | '/spa' | '/spa_/detail' + to: '/' | '/mpa' | '/spa' | '/mpa/detail' | '/spa/detail' + id: '__root__' | '/' | '/mpa' | '/spa' | '/mpa_/detail' | '/spa_/detail' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + MpaRoute: typeof MpaRoute SpaRoute: typeof SpaRoute + MpaDetailRoute: typeof MpaDetailRoute SpaDetailRoute: typeof SpaDetailRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/mpa': { + id: '/mpa' + path: '/mpa' + fullPath: '/mpa' + preLoaderRoute: typeof MpaRouteImport + parentRoute: typeof rootRouteImport + } '/spa': { id: '/spa' path: '/spa' @@ -75,6 +102,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/mpa_/detail': { + id: '/mpa_/detail' + path: '/mpa/detail' + fullPath: '/mpa/detail' + preLoaderRoute: typeof MpaDetailRouteImport + parentRoute: typeof rootRouteImport + } '/spa_/detail': { id: '/spa_/detail' path: '/spa/detail' @@ -87,7 +121,9 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + MpaRoute: MpaRoute, SpaRoute: SpaRoute, + MpaDetailRoute: MpaDetailRoute, SpaDetailRoute: SpaDetailRoute, } export const routeTree = rootRouteImport diff --git a/packages/app-tanstack-start-react/src/routes/mpa.tsx b/packages/app-tanstack-start-react/src/routes/mpa.tsx new file mode 100644 index 00000000..9a2da7f7 --- /dev/null +++ b/packages/app-tanstack-start-react/src/routes/mpa.tsx @@ -0,0 +1,27 @@ +import { createFileRoute } from '@tanstack/react-router' +import { testData } from '../../../testdata/src/ssr' + +export const Route = createFileRoute('/mpa')({ + component: MpaPage, + loader: async () => await testData(), +}) + +function MpaPage() { + const data = Route.useLoaderData() + + return ( + + + {data.map((entry) => ( + + + + + + ))} + +
{entry.id}{entry.name} + View → +
+ ) +} diff --git a/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx b/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx new file mode 100644 index 00000000..2db2d45c --- /dev/null +++ b/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/mpa_/detail')({ + loaderDeps: ({ search }) => ({ id: search.id }), + loader: ({ deps }) => { + return { id: deps.id as string | undefined } + }, + component: MpaDetailPage, +}) + +function MpaDetailPage() { + const { id } = Route.useLoaderData() + + return

{id}

+} From 869d296597704fa6af199b7a9359128d8a800919 Mon Sep 17 00:00:00 2001 From: Alexander Karan Date: Mon, 20 Apr 2026 13:37:32 +0800 Subject: [PATCH 2/3] Format --- packages/app-astro/src/pages/mpa.astro | 6 ++---- .../app-tanstack-start-react/src/routes/mpa_.detail.tsx | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/app-astro/src/pages/mpa.astro b/packages/app-astro/src/pages/mpa.astro index 12b05326..e5415e42 100644 --- a/packages/app-astro/src/pages/mpa.astro +++ b/packages/app-astro/src/pages/mpa.astro @@ -1,8 +1,6 @@ --- -const entries = Array.from({ length: 1000 }, () => ({ - id: crypto.randomUUID(), - name: crypto.randomUUID(), -})) +import { testData } from '../../../testdata/src/ssr' +const entries = await testData() --- diff --git a/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx b/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx index 2db2d45c..1022a15e 100644 --- a/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx +++ b/packages/app-tanstack-start-react/src/routes/mpa_.detail.tsx @@ -1,15 +1,14 @@ import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/mpa_/detail')({ - loaderDeps: ({ search }) => ({ id: search.id }), - loader: ({ deps }) => { - return { id: deps.id as string | undefined } - }, + validateSearch: (search) => ({ + id: typeof search.id === 'string' ? search.id : undefined, + }), component: MpaDetailPage, }) function MpaDetailPage() { - const { id } = Route.useLoaderData() + const { id } = Route.useSearch() return

{id}

} From b546e5e6e8d558cd5a2a675c66062e33c4447c74 Mon Sep 17 00:00:00 2001 From: Alexander Karan Date: Wed, 22 Apr 2026 11:44:02 +0800 Subject: [PATCH 3/3] Updated from running tests --- packages/app-react-router/app/routes.ts | 8 ++- .../app-sveltekit/src/routes/mpa/+page.ts | 1 + .../src/routes/mpa/detail/+page.ts | 1 + .../src/routeTree.gen.ts | 52 +++++++++---------- 4 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 packages/app-sveltekit/src/routes/mpa/+page.ts create mode 100644 packages/app-sveltekit/src/routes/mpa/detail/+page.ts diff --git a/packages/app-react-router/app/routes.ts b/packages/app-react-router/app/routes.ts index 1c1d80fa..b42b4611 100644 --- a/packages/app-react-router/app/routes.ts +++ b/packages/app-react-router/app/routes.ts @@ -6,6 +6,10 @@ export default [ ...(isSpa ? [] : [index('routes/home.tsx')]), route('/spa', 'routes/spa.tsx'), route('/spa/detail', 'routes/spa.detail.tsx'), - route('/mpa', 'routes/mpa.tsx'), - route('/mpa/detail', 'routes/mpa.detail.tsx'), + ...(isSpa + ? [] + : [ + route('/mpa', 'routes/mpa.tsx'), + route('/mpa/detail', 'routes/mpa.detail.tsx'), + ]), ] satisfies RouteConfig diff --git a/packages/app-sveltekit/src/routes/mpa/+page.ts b/packages/app-sveltekit/src/routes/mpa/+page.ts new file mode 100644 index 00000000..3013ed92 --- /dev/null +++ b/packages/app-sveltekit/src/routes/mpa/+page.ts @@ -0,0 +1 @@ +export const csr = false diff --git a/packages/app-sveltekit/src/routes/mpa/detail/+page.ts b/packages/app-sveltekit/src/routes/mpa/detail/+page.ts new file mode 100644 index 00000000..3013ed92 --- /dev/null +++ b/packages/app-sveltekit/src/routes/mpa/detail/+page.ts @@ -0,0 +1 @@ +export const csr = false diff --git a/packages/app-tanstack-start-react/src/routeTree.gen.ts b/packages/app-tanstack-start-react/src/routeTree.gen.ts index cdbd5daa..7d7bff2a 100644 --- a/packages/app-tanstack-start-react/src/routeTree.gen.ts +++ b/packages/app-tanstack-start-react/src/routeTree.gen.ts @@ -9,37 +9,37 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' -import { Route as MpaRouteImport } from './routes/mpa' import { Route as SpaRouteImport } from './routes/spa' +import { Route as MpaRouteImport } from './routes/mpa' import { Route as IndexRouteImport } from './routes/index' -import { Route as MpaDetailRouteImport } from './routes/mpa_.detail' import { Route as SpaDetailRouteImport } from './routes/spa_.detail' +import { Route as MpaDetailRouteImport } from './routes/mpa_.detail' -const MpaRoute = MpaRouteImport.update({ - id: '/mpa', - path: '/mpa', - getParentRoute: () => rootRouteImport, -} as any) const SpaRoute = SpaRouteImport.update({ id: '/spa', path: '/spa', getParentRoute: () => rootRouteImport, } as any) +const MpaRoute = MpaRouteImport.update({ + id: '/mpa', + path: '/mpa', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) -const MpaDetailRoute = MpaDetailRouteImport.update({ - id: '/mpa_/detail', - path: '/mpa/detail', - getParentRoute: () => rootRouteImport, -} as any) const SpaDetailRoute = SpaDetailRouteImport.update({ id: '/spa_/detail', path: '/spa/detail', getParentRoute: () => rootRouteImport, } as any) +const MpaDetailRoute = MpaDetailRouteImport.update({ + id: '/mpa_/detail', + path: '/mpa/detail', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -81,13 +81,6 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/mpa': { - id: '/mpa' - path: '/mpa' - fullPath: '/mpa' - preLoaderRoute: typeof MpaRouteImport - parentRoute: typeof rootRouteImport - } '/spa': { id: '/spa' path: '/spa' @@ -95,6 +88,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SpaRouteImport parentRoute: typeof rootRouteImport } + '/mpa': { + id: '/mpa' + path: '/mpa' + fullPath: '/mpa' + preLoaderRoute: typeof MpaRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -102,13 +102,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/mpa_/detail': { - id: '/mpa_/detail' - path: '/mpa/detail' - fullPath: '/mpa/detail' - preLoaderRoute: typeof MpaDetailRouteImport - parentRoute: typeof rootRouteImport - } '/spa_/detail': { id: '/spa_/detail' path: '/spa/detail' @@ -116,6 +109,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SpaDetailRouteImport parentRoute: typeof rootRouteImport } + '/mpa_/detail': { + id: '/mpa_/detail' + path: '/mpa/detail' + fullPath: '/mpa/detail' + preLoaderRoute: typeof MpaDetailRouteImport + parentRoute: typeof rootRouteImport + } } }