SuperAPI AI Directory

tanstack-query-intent-compositions-compose-query-with-t-d0bcdebe

Frontend & websites

View repository

Updated 10-16 09:23

About this skill

Setup

import {
  QueryClient,
  QueryClientProvider,
  useSuspenseQuery,
} from '@tanstack/react-query'
import {
  createFileRoute,
  createRouter,
  RouterProvider,
} from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const queryClient = new QueryClient()

const postsQuery = { queryKey: ['posts'], queryFn: async () => [{ id: 1, title: 'Router first' }], }

export const Route = createFileRoute('/posts')({ loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery), component: PostsPage, })

function PostsPage() { const { data } = useSuspenseQuery(postsQuery) return {JSON.stringify(data)} }

const router = createRouter({ routeTree, context: { queryClient } })

export function App() { return ( ) }


## Core Integration Patterns

### Put QueryClient in router context

```ts
import { QueryClient } from '@tanstack/react-query'
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const queryClient = new QueryClient()

export const router = createRouter({
  routeTree,
  context: { queryClient },
  defaultPreloadStaleTime: 0,
})

Set defaultPreloadStaleTime: 0 when Query owns server-state freshness. Router still preloads and runs loaders, but Query is the cache authority.

Use loader ensureQueryData for route data

import { createFileRoute } from '@tanstack/react-router'

const todoQuery = (todoId: string) => ({
  queryKey: ['todo', todoId],
  queryFn: async () => ({ id: todoId, title: 'Loaded' }),
})

export const Route = createFileRoute('/todos/$todoId')({
  loader: ({ context, params }) =>
    context.queryClient.ensureQueryData(todoQuery(params.todoId)),
})

Treat loaders as event handlers that prime the Query cache. Components should still call useQuery or useSuspenseQuery so the route has an active Query observer.

Prefer Router SSR Query integration for Router SSR

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'

const queryClient = new QueryClient()
const router = createRouter({ routeTree, context: { queryClient } })

setupRouterSsrQueryIntegration({ router, queryClient })

Common Mistakes

CRITICAL Component-only Query creates route waterfall

Wrong:

import { createFileRoute } from '@tanstack/react-router'
import { useSuspenseQuery } from '@tanstack/react-query'

export const Route = createFileRoute('/posts')({ component: PostsPage })
function PostsPage() {
  const { data } = useSuspenseQuery({
    queryKey: ['posts'],
    queryFn: async () => [{ id: 1 }],
  })
  return <pre>{JSON.stringify(data)}</pre>
}

Correct:

import { createFileRoute } from '@tanstack/react-router'
import { useSuspenseQuery } from '@tanstack/react-query'

const postsQuery = { queryKey: ['posts'], queryFn: async () => [{ id: 1 }] }
export const Route = createFileRoute('/posts')({
  loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
  component: PostsPage,
})
function PostsPage() {
  const { data } = useSuspenseQuery(postsQuery)
  return <pre>{JSON.stringify(data)}</pre>
}

The router can load route-critical data before component render.

Source: TanStack/router:https://tanstack.com/router/latest/docs/integrations/query

CRITICAL Hand-rolled Router hydration

Wrong:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createRouter, RouterProvider } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const queryClient = new QueryClient()
const router = createRouter({ routeTree })
export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <RouterProvider router={router} />
    </QueryClientProvider>
  )
}

Correct:

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'

const queryClient = new QueryClient()
const router = createRouter({ routeTree, context: { queryClient } })
setupRouterSsrQueryIntegration({ router, queryClient })

The Router SSR Query integration handles dehydration, hydration, streamed query results, redirects, and provider wrapping.

Source: TanStack/router:https://tanstack.com/router/latest/docs/integrations/query

HIGH Next.js mental model copied into Start

Wrong:

export default async function Page() {
  const posts = await Promise.resolve([{ id: 1 }])
  return <pre>{JSON.stringify(posts)}</pre>
}

Correct:

import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  loader: ({ context }) =>
    context.queryClient.ensureQueryData({
      queryKey: ['posts'],
      queryFn: async () => [{ id: 1 }],
    }),
  component: () => <p>Posts loaded by the route</p>,
})

Start is powered by TanStack Router; use file routes, loaders, server functions, and Router SSR before Next-specific app/pages APIs.

Source: TanStack/start:https://tanstack.com/start/latest/docs/framework/react/overview

HIGH Reading loader data instead of observing Query

Wrong:

import { createFileRoute, useLoaderData } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
  component: PostsPage,
})

function PostsPage() {
  const posts = useLoaderData({ from: '/posts' })
  return <pre>{JSON.stringify(posts)}</pre>
}

Correct:

import { useSuspenseQuery } from '@tanstack/react-query'

function PostsPage() {
  const { data } = useSuspenseQuery(postsQuery)
  return <pre>{JSON.stringify(data)}</pre>
}

The loader primes the cache. The component still needs an active Query observer for refetch triggers, invalidation, and garbage collection semantics.

Source: https://tkdodo.eu/blog/tan-stack-router-and-query

See also: lifecycle/ssr-hydration-and-streaming for framework SSR recipes.