Browse documentation
Agent docs/query/loader-prefetch
recipe

Prefetch Query from a route loader

Create a request-scoped QueryClient for SSR and reuse one queryOptions contract in the loader and useSuspenseQuery.

View raw Markdown
CONTRACT

Execution contract

Use when
Use when a route should warm Query data before entry and the component will observe the same cache entry.
Avoid when
Do not block a loader on expensive, rarely visited, or non-critical data.
Preconditions
Create a shared queryOptions factory and include every route input affecting the query key in loader deps.
Verification
Navigation issues one request, the component hits the same cache, and parameter changes create a distinct predictable key.
Failure mode
If the component refetches immediately, compare full query keys, freshness settings, and QueryClient identity.
Security
Prefetching does not hide responses; never place unauthorized data in serialized cache or SSR payloads.
01

Required integration

Create QueryClient inside getRouter, expose it through Router context, then call setupRouterSsrQueryIntegration. getRouter runs per SSR request; do not hoist QueryClient into a module singleton.

src/router.tsxtsx
const queryClient = new QueryClient()
const router = createRouter({
  routeTree,
  context: { queryClient },
})

setupRouterSsrQueryIntegration({ router, queryClient })
02

Blocking critical data

For render-critical data, return the ensureQueryData promise from the loader. Call useSuspenseQuery with the same queryOptions in the component. Router waits for navigation, Query owns cache, and the SSR integration handles dehydration and hydration.

src/routes/issues/index.tsxtsx
const issuesQuery = queryOptions({
  queryKey: ['issues'],
  queryFn: getIssues,
})

export const Route = createFileRoute('/issues/')({
  loader: ({ context }) =>
    context.queryClient.ensureQueryData(issuesQuery),
  component: () => {
    const { data } = useSuspenseQuery(issuesQuery)
    return <IssueList issues={data} />
  },
})
03

Verification checklist

Confirm root Router context types QueryClient, no QueryClient is shared across SSR requests, loader and component use an identical queryKey, and the hydrated page does not repeat its initial browser request.

PRIMARY SOURCEShttps://tanstack.com/router/latest/docs/integrations/query
TanStack Atlas

Original bilingual knowledge · verified against primary sources

Friend linksGitHub