Repository navigation
Replies: 3 comments 1 reply
|
There's a documented pattern for exactly this in the router-context guide, and it sidesteps the three approaches you listed because it doesn't try to make export const Route = createRootRoute({
component: () => {
const matches = useRouterState({ select: (s) => s.matches })
const matchWithTitle = [...matches].reverse().find((d) => d.context.getTitle)
const title = matchWithTitle?.context.getTitle() || 'My App'
return (
<html>
<head><title>{title}</title></head>
<body>{/* ... */}</body>
</html>
)
},
})The key difference from This is also why it avoids your two failure modes: it goes through the router's own |
|
Worth adding that this isn't SPA-only — it bites SSR apps too. If a route awaits its loader on the server but skips it on the client to keep loader: ({ context, params }) => {
if(typeof document === undefined) {
const q = await context.queryClient.query(postQuery(params.postId))
return q
}
return undefined
},
head: ({ loaderData }) =>
loaderData ? { meta: [{ title: loaderData.title }] } : {},then So you get correct metadata for crawlers and a hard refresh, and stale metadata for |
|
+1 on this, and a gentle bump for maintainer input. 🙏 Our use case (TanStack Start, SSR streaming): on listing/region pages the title, description, OG image and canonical depend on loader data, so today we have to It would help to know the intended direction:
Even a short answer would help us (and others) decide whether to wait for an API or design around it. |
Uh oh!
There was an error while loading. Please reload this page.
Problem
In a SPA using the recommended TanStack Query integration, loaders typically call
queryClient.ensureQueryData()without awaiting so navigation stays instant, and components read the data withuseSuspenseQuery. In this setuphead({ loaderData })can't produce a dynamic title:loaderDatais empty whenhead()runs, andhead()is never re-invoked once the data lands in the cache.Current options all have drawbacks:
useEffect+document.titleafter Suspense resolves → works, but bypassesHeadContent, and only by relying on the (undocumented) fact thatHeadContentwon't rewrite an unchanged title.<title>hoisting → conflicts with the<title>thatHeadContentrenders (duplicate tags, first-in-document wins).Proposal
First-class support for a two-phase title: a static fallback shown at navigation, replaced when async data resolves. Possible shapes:
head()when an unawaited/deferred promise returned from the loader resolves (Await-awarehead), oruseHead()hook or<RouteHead title={...} />component that participates inHeadContent's dedupe/override chain.Related
document.titleworkaroundheadrunning before the loader finishes (the awaited case)All reactions