Connect React Query to TanStack Start for server rendering, route preloading, hydration, and cached client navigation.
Route loaders prepare query data before rendering. Components read the same query options, while the Router integration transfers server-fetched query data to the browser. Follow the post list into a detail page to see how route loading and query caching work together.
Use Node.js 22.12 or newer and pnpm 11. These examples fetch public sample data from JSONPlaceholder, so an internet connection is required. No API key or database is needed.
git clone https://github.com/TanStack/router.git
cd router
pnpm install
cd examples/react/start-basic-react-query
pnpm devOpen the local URL printed by Vite. Visit a post directly, navigate back to the list, and inspect query state with the included devtools.
Run pnpm build from the example directory to build the app and check its types.
/// <reference types="vite/client" />
import {
HeadContent,
Link,
Outlet,
Scripts,
createRootRouteWithContext,
} from '@tanstack/react-router'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import * as React from 'react'
import type { QueryClient } from '@tanstack/react-query'
import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'
import { NotFound } from '~/components/NotFound'
import appCss from '~/styles/app.css?url'
import { seo } from '~/utils/seo'
export const Route = createRootRouteWithContext<{
queryClient: QueryClient
}>()({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
...seo({
title:
'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework',
description: `TanStack Start is a type-safe, client-first, full-stack React framework. `,
}),
],
links: [
{ rel: 'stylesheet', href: appCss },
{
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/apple-touch-icon.png',
},
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: '/favicon-32x32.png',
},
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: '/favicon-16x16.png',
},
{ rel: 'manifest', href: '/site.webmanifest', color: '#fffff' },
{ rel: 'icon', href: '/favicon.ico' },
],
}),
errorComponent: (props) => {
return (
<RootDocument>
<DefaultCatchBoundary {...props} />
</RootDocument>
)
},
notFoundComponent: () => <NotFound />,
component: RootComponent,
})
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
)
}
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
<div className="p-2 flex gap-2 text-lg">
<Link to="/preferences">Preferences</Link>
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>{' '}
<Link
to="/posts"
activeProps={{
className: 'font-bold',
}}
>
Posts
</Link>{' '}
<Link
to="/users"
activeProps={{
className: 'font-bold',
}}
>
Users
</Link>{' '}
<Link
to="/route-a"
activeProps={{
className: 'font-bold',
}}
>
Pathless Layout
</Link>{' '}
<Link
to="/deferred"
activeProps={{
className: 'font-bold',
}}
>
Deferred
</Link>{' '}
<Link
// @ts-expect-error
to="/this-route-does-not-exist"
activeProps={{
className: 'font-bold',
}}
>
This Route Does Not Exist
</Link>
</div>
<hr />
{children}
<TanStackRouterDevtools position="bottom-right" />
<ReactQueryDevtools buttonPosition="bottom-left" />
<Scripts />
</body>
</html>
)
}