Wichtig
Diese Integration automatisiert SSR Dehydration/Hydration und Streaming zwischen TanStack Router und TanStack Query. Wenn Sie den Standardleitfaden für External Data Loading noch nicht gelesen haben, beginnen Sie dort.
Die TanStack Query-Integration ist ein separates Paket, das Sie installieren müssen
npm install -D @tanstack/react-router-ssr-query
# or
pnpm add -D @tanstack/react-router-ssr-query
# or
yarn add -D @tanstack/react-router-ssr-query
# or
bun add -D @tanstack/react-router-ssr-query
npm install -D @tanstack/react-router-ssr-query
# or
pnpm add -D @tanstack/react-router-ssr-query
# or
yarn add -D @tanstack/react-router-ssr-query
# or
bun add -D @tanstack/react-router-ssr-query
Erstellen Sie Ihren Router und verdrahten Sie die Integration. Stellen Sie sicher, dass in SSR-Umgebungen pro Anfrage ein frischer QueryClient erstellt wird.
// src/router.tsx
import { QueryClient } from '@tanstack/react-query'
import { createRouter } from '@tanstack/react-router'
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
import { routeTree } from './routeTree.gen'
export function createAppRouter() {
const queryClient = new QueryClient()
const router = createRouter({
routeTree,
// optionally expose the QueryClient via router context
context: { queryClient },
scrollRestoration: true,
defaultPreload: 'intent',
})
setupRouterSsrQueryIntegration({
router,
queryClient,
// optional:
// handleRedirects: true,
// wrapQueryClient: true,
})
return router
}
// src/router.tsx
import { QueryClient } from '@tanstack/react-query'
import { createRouter } from '@tanstack/react-router'
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
import { routeTree } from './routeTree.gen'
export function createAppRouter() {
const queryClient = new QueryClient()
const router = createRouter({
routeTree,
// optionally expose the QueryClient via router context
context: { queryClient },
scrollRestoration: true,
defaultPreload: 'intent',
})
setupRouterSsrQueryIntegration({
router,
queryClient,
// optional:
// handleRedirects: true,
// wrapQueryClient: true,
})
return router
}
Standardmäßig wickelt die Integration Ihren Router mit einem QueryClientProvider. Wenn Sie bereits Ihren eigenen Provider bereitstellen, übergeben Sie wrapQueryClient: false und behalten Sie Ihren benutzerdefinierten Wrapper bei.
// Suspense: executes on server and streams
const { data } = useSuspenseQuery(postsQuery)
// Non-suspense: executes only on client
const { data, isLoading } = useQuery(postsQuery)
// Suspense: executes on server and streams
const { data } = useSuspenseQuery(postsQuery)
// Non-suspense: executes only on client
const { data, isLoading } = useQuery(postsQuery)
Preload kritische Daten im loader der Route, um Wasserfälle und Ladeanzeigen zu vermeiden, und lesen Sie sie dann in der Komponente. Die Integration stellt sicher, dass serverseitig abgerufene Daten während SSR dehydriert und zum Client gestreamt werden.
// src/routes/posts.tsx
import { queryOptions, useSuspenseQuery, useQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
const postsQuery = queryOptions({
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then((r) => r.json()),
})
export const Route = createFileRoute('/posts')({
// Ensure the data is in the cache before render
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
component: PostsPage,
})
function PostsPage() {
// Prefer suspense for best SSR + streaming behavior
const { data } = useSuspenseQuery(postsQuery)
return <div>{data.map((p: any) => p.title).join(', ')}</div>
}
// src/routes/posts.tsx
import { queryOptions, useSuspenseQuery, useQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
const postsQuery = queryOptions({
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then((r) => r.json()),
})
export const Route = createFileRoute('/posts')({
// Ensure the data is in the cache before render
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
component: PostsPage,
})
function PostsPage() {
// Prefer suspense for best SSR + streaming behavior
const { data } = useSuspenseQuery(postsQuery)
return <div>{data.map((p: any) => p.title).join(', ')}</div>
}
Sie können auch mit fetchQuery oder ensureQueryData in einem Loader prefetchen, ohne die Daten in einer Komponente zu konsumieren. Wenn Sie das Promise direkt vom Loader zurückgeben, wird es erwartet und blockiert somit die SSR-Anfrage, bis die Query abgeschlossen ist. Wenn Sie das Promise weder erwarten noch zurückgeben, wird die Query auf dem Server gestartet und zum Client gestreamt, ohne die SSR-Anfrage zu blockieren.
import { createFileRoute } from '@tanstack/react-router'
import { queryOptions, useQuery } from '@tanstack/react-query'
const userQuery = (id: string) =>
queryOptions({
queryKey: ['user', id],
queryFn: () => fetch(`/api/users/${id}`).then((r) => r.json()),
})
export const Route = createFileRoute('/user/$id')({
loader: ({ params }) => {
// do not await this nor return the promise, just kick off the query to stream it to the client
context.queryClient.fetchQuery(userQuery(params.id))
},
})
import { createFileRoute } from '@tanstack/react-router'
import { queryOptions, useQuery } from '@tanstack/react-query'
const userQuery = (id: string) =>
queryOptions({
queryKey: ['user', id],
queryFn: () => fetch(`/api/users/${id}`).then((r) => r.json()),
})
export const Route = createFileRoute('/user/$id')({
loader: ({ params }) => {
// do not await this nor return the promise, just kick off the query to stream it to the client
context.queryClient.fetchQuery(userQuery(params.id))
},
})
Wenn eine Query oder Mutation einen redirect(...) wirft, fängt die Integration ihn auf dem Client ab und führt eine Router-Navigation durch.
TanStack Start verwendet TanStack Router unter der Haube. Die gleiche Einrichtung gilt, und die Integration streamt Query-Ergebnisse während SSR automatisch.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.