Skip to content

Commit 812792f

Browse files
fix: reduce start SSR manifest asset duplication (#7157)
Co-authored-by: schiller-manuel <schiller-manuel@users.noreply.github.com> Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
1 parent 5cec6fb commit 812792f

33 files changed

Lines changed: 1272 additions & 6 deletions

.changeset/cyan-camels-dance.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@tanstack/router-core': patch
3+
'@tanstack/start-plugin-core': patch
4+
'@tanstack/start-server-core': patch
5+
---
6+
7+
Reduce React Start SSR manifest payload size by omitting unmatched route assets from dehydrated router state while keeping start-manifest asset serialization deduplicated by shared object identity.
8+
9+
This improves SSR HTML size for apps with many routes that share the same CSS assets and adds regression coverage for CSS module hydration, navigation, and start-manifest asset reuse.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "tanstack-react-start-e2e-start-manifest",
3+
"private": true,
4+
"sideEffects": false,
5+
"type": "module",
6+
"scripts": {
7+
"build": "vite build && tsc --noEmit",
8+
"start": "node .output/server/index.mjs",
9+
"test:e2e": "playwright test --project=chromium"
10+
},
11+
"dependencies": {
12+
"@tanstack/react-router": "workspace:^",
13+
"@tanstack/react-start": "workspace:^",
14+
"react": "^19.0.0",
15+
"react-dom": "^19.0.0"
16+
},
17+
"devDependencies": {
18+
"@playwright/test": "^1.50.1",
19+
"@tanstack/router-e2e-utils": "workspace:^",
20+
"@types/node": "^22.10.2",
21+
"@types/react": "^19.0.8",
22+
"@types/react-dom": "^19.0.3",
23+
"@vitejs/plugin-react": "^6.0.1",
24+
"nitro": "^3.0.260311-beta",
25+
"typescript": "^6.0.2",
26+
"vite": "^8.0.0"
27+
},
28+
"nx": {
29+
"targets": {
30+
"test:e2e": {
31+
"parallelism": false
32+
}
33+
}
34+
}
35+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
import { getTestServerPort } from '@tanstack/router-e2e-utils'
3+
import packageJson from './package.json' with { type: 'json' }
4+
5+
const PORT = await getTestServerPort(packageJson.name)
6+
const baseURL = `http://localhost:${PORT}`
7+
8+
export default defineConfig({
9+
testDir: './tests',
10+
workers: 1,
11+
reporter: [['line']],
12+
13+
use: {
14+
baseURL,
15+
},
16+
17+
webServer: {
18+
command: `PORT=${PORT} pnpm build && PORT=${PORT} pnpm start`,
19+
url: baseURL,
20+
reuseExistingServer: !process.env.CI,
21+
stdout: 'pipe',
22+
},
23+
24+
projects: [
25+
{
26+
name: 'chromium',
27+
use: { ...devices['Desktop Chrome'] },
28+
},
29+
],
30+
})
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ClientOnly, Link, Outlet, linkOptions } from '@tanstack/react-router'
2+
import styles from '~/styles/root-shell.module.css'
3+
4+
const ROUTES = linkOptions([
5+
{ to: '/', label: 'home' },
6+
{ to: '/r1', label: '/r1' },
7+
{ to: '/r2', label: '/r2' },
8+
{ to: '/shared-a', label: '/shared-a' },
9+
])
10+
11+
export function AppShell() {
12+
return (
13+
<div className={styles.shell}>
14+
<nav className={styles.nav}>
15+
{ROUTES.map((route) => (
16+
<Link key={route.to} {...route} data-testid={`nav-${route.label}`}>
17+
{route.label}
18+
</Link>
19+
))}
20+
</nav>
21+
22+
<main className={styles.content}>
23+
<div className={styles.rootBadge} data-testid="root-shell-marker">
24+
Start manifest CSS root shell
25+
</div>
26+
<ClientOnly>
27+
<div data-testid="hydration-marker">hydrated</div>
28+
</ClientOnly>
29+
<Outlet />
30+
</main>
31+
</div>
32+
)
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Link, linkOptions } from '@tanstack/react-router'
2+
import type { ReactNode } from 'react'
3+
import styles from '~/styles/shared-layout.module.css'
4+
5+
const SHARED_ROUTES = linkOptions([
6+
{ to: '/shared-a', label: '/shared-a' },
7+
{ to: '/shared-b', label: '/shared-b' },
8+
{ to: '/shared-c', label: '/shared-c' },
9+
])
10+
11+
export function SharedNestedLayout({ children }: { children: ReactNode }) {
12+
return (
13+
<section className={styles.layout} data-testid="shared-layout-shell">
14+
<div className={styles.heading} data-testid="shared-layout-heading">
15+
Shared nested layout CSS module
16+
</div>
17+
<nav>
18+
{SHARED_ROUTES.map((route) => (
19+
<Link key={route.to} {...route} data-testid={`nav-${route.label}`}>
20+
{route.label}
21+
</Link>
22+
))}
23+
</nav>
24+
<div className={styles.body} data-testid="shared-layout-copy">
25+
{children}
26+
</div>
27+
</section>
28+
)
29+
}

0 commit comments

Comments
 (0)