From 71eba8c284d64d06e647ef2c07765a56b8cad4cb Mon Sep 17 00:00:00 2001 From: unpunnyfuns Date: Wed, 15 Jul 2026 22:28:10 +0200 Subject: [PATCH] fix(tanstack-react): keep TanStack Start's document shell out of stories --- .../src/routing/duplicate-tree.test.ts | 21 ++++++++++++++++++- .../src/routing/duplicate-tree.ts | 12 ++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts b/code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts index c9860e7120a4..a7bc07b9f3eb 100644 --- a/code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts +++ b/code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { createMemoryHistory, createRootRoute, @@ -34,6 +34,25 @@ function buildAuthedTree(layoutOptions: Record = {}) { return { root, layout, dashboard }; } +describe('duplicateRouteTree root options', () => { + it('copies root behavior but never the document shell', () => { + const RootComponent = () => null; + const rootBeforeLoad = vi.fn(); + const Shell = ({ children }: { children: unknown }) => children; + const root = createRootRoute({ + component: RootComponent, + beforeLoad: rootBeforeLoad, + shellComponent: Shell, + } as any); + + const { root: cloned } = duplicateRouteTree(root as any); + + expect((cloned as any).options.component).toBe(RootComponent); + expect((cloned as any).options.beforeLoad).toBe(rootBeforeLoad); + expect((cloned as any).options.shellComponent).toBeUndefined(); + }); +}); + describe('duplicateRouteTree with pathless layout routes', () => { it('preserves a code-based pathless layout (explicit id, no path)', async () => { const root = createRootRoute(); diff --git a/code/frameworks/tanstack-react/src/routing/duplicate-tree.ts b/code/frameworks/tanstack-react/src/routing/duplicate-tree.ts index a6738d042c40..600caf9f99c3 100644 --- a/code/frameworks/tanstack-react/src/routing/duplicate-tree.ts +++ b/code/frameworks/tanstack-react/src/routing/duplicate-tree.ts @@ -129,7 +129,17 @@ export function duplicateRouteTree( // one story is mounted in the same browser session (e.g. HMR, navigation). // We strip `id` from spread options so TanStack assigns the canonical // `__root__` id itself. - const { id: _rootId, getParentRoute: _rootGetParent, ...restRoot } = rootOptions; + // `shellComponent` is also stripped: it is TanStack Start's document shell + // (`html`/`head`/`body`), which React refuses to nest inside the story + // canvas and whose head content hoists into the page, hijacking the + // document title from inside a story. All other root behavior (component, + // notFoundComponent, errorComponent, beforeLoad context) is kept. + const { + id: _rootId, + getParentRoute: _rootGetParent, + shellComponent: _rootShell, + ...restRoot + } = rootOptions; const newRoot = createRootRouteWithContext()({ ...restRoot, ...rootOverride,