---
id: query.ssr-streaming
kind: contract
product: query
framework: react
locale: en
revision: 1
sourceCheckedOn: "2026-08-03"
versionRange: "Query ^5 / Router ^1"
verifiedAgainst: "@tanstack/react-query@5.101.4 + @tanstack/react-router@1.170.18 + @tanstack/react-router-ssr-query@1.167.1"
contentModel: 2
packages:
  - "@tanstack/react-query"
  - "@tanstack/react-router"
  - "@tanstack/react-router-ssr-query"
tasks:
  - "configure-query-ssr"
  - "stream-query-data"
  - "prevent-hydration-refetch"
sourceRefs:
  - "https://tanstack.com/router/latest/docs/integrations/query"
  - "https://tanstack.com/query/latest/docs/framework/react/guides/ssr"
---

# 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.

## 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.

## 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.

File: `src/router.tsx`

```tsx
export function getRouter() {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { staleTime: 30_000 } },
  })
  const router = createRouter({ routeTree, context: { queryClient } })
  setupRouterSsrQueryIntegration({ router, queryClient })
  return router
}
```

## 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.

## 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.

## Official sources

- https://tanstack.com/router/latest/docs/integrations/query
- https://tanstack.com/query/latest/docs/framework/react/guides/ssr
