Browse documentation
Agent docs/query/ssr-streaming
contract

Configure Query SSR and streaming hydration

Create a QueryClient per SSR request, use the Router integration for dehydration and hydration, and classify blocking versus streaming queries.

View raw Markdown
CONTRACT

Execution contract

Use when
Use when a Router or Start app prefetches Query during SSR and needs automatic dehydration, hydration, or streaming.
Avoid when
A pure SPA or client-only data does not need SSR serialization; do not add server hydration complexity without value.
Preconditions
Confirm getRouter runs per request, QueryClient ownership, critical versus optional query classes, and the fields allowed in browser payloads.
Verification
Use concurrent user sessions to verify cache isolation, inspect HTML/stream payloads for secrets, and confirm hydrated queries do not immediately refetch.
Failure mode
Cross-request data indicates a module-level QueryClient singleton; an immediate hydration refetch usually means query keys, instances, or staleTime disagree.
Security
Dehydrated cache is browser-visible; authorize before fetching, serialize minimum fields, and rely on the officially escaped integration for HTML context.
01

Instance ownership

Create QueryClient inside getRouter and pass the same instance to Router Context and the integration. Never hoist a user-data QueryClient into a server module singleton. Set a suitable staleTime for SSR data so hydration does not immediately refetch it as stale.

src/router.tsxtsx
export function getRouter() {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { staleTime: 30_000 } },
  })
  const router = createRouter({ routeTree, context: { queryClient } })
  setupRouterSsrQueryIntegration({ router, queryClient })
  return router
}
02

Loading modes

Return or await ensureQueryData in the loader for critical data. Start fetchQuery without returning or awaiting it for data that may arrive progressively. Use useSuspenseQuery for SSR participation; plain useQuery fetches on the client after hydration. Do not sequentially await independent queries.

03

Payload audit

Dehydrate only browser-readable data and use shouldDehydrateQuery to exclude explicitly marked entries. Inspect HTML and stream payloads for secrets, server-only fields, or other-user data. Custom serialization must escape HTML context; do not concatenate raw JSON.stringify output.

PRIMARY SOURCEShttps://tanstack.com/router/latest/docs/integrations/queryhttps://tanstack.com/query/latest/docs/framework/react/guides/ssr
TanStack Atlas

Original bilingual knowledge · verified against primary sources

Friend linksGitHub