nextjs tanstack query
via PatrickJS/awesome-cursorrules
Next.js App Router + TanStack Query v5 with HydrationBoundary, Server Actions, and optimistic updates
What is nextjs tanstack query?
Integrates TanStack Query v5 into Next.js App Router by prefetching data in Server Components, hydrating the client cache via HydrationBoundary, and using Server Actions as mutation handlers. Use this when building interactive pages that need server-side initial data, real-time updates, optimistic UI, or infinite scroll.
- Prefetch queries in Server Components and hydrate client cache to eliminate waterfalls
- Use Server Actions as mutationFn for mutations with automatic revalidation
- Implement optimistic updates with onMutate/onError/onSettled lifecycle
- Support infinite scroll and polling in Client Components with pre-populated cache
- Manage separate QueryClient instances per request (server) and per session (browser)
- Combine React Server Components for initial data with TanStack Query for interactive state
Applies to
File patterns this rule matches.
Rule definition (reference)
Source of truth, from the repository.
You are an expert in Next.js App Router, TanStack Query v5, TypeScript, and combining server components with client-side data fetching.
Architecture
- Server Components fetch data directly — no TanStack Query needed there
- TanStack Query lives in Client Components for interactive, real-time, or mutation-driven data
- Hydrate the Query cache from server to avoid client waterfalls on first load
- Use React Server Components for initial page data; TanStack Query for mutations + polling + optimistic UI
Provider Setup
// providers/query-provider.tsx
'use client'
export function QueryProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: { queries: { staleTime: 60 * 1000 } },
}))
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Server Prefetch + HydrationBoundary Pattern
// app/posts/page.tsx (Server Component)
export default async function PostsPage() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(postsQueryOptions())
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<PostsList />
</HydrationBoundary>
)
}
// app/posts/_components/posts-list.tsx (Client Component)
'use client'
export function PostsList() {
const { data: posts } = useQuery(postsQueryOptions()) // reads from pre-populated cache
return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}
queryOptions Factory
export const postsQueryOptions = (filters?: PostFilters) =>
queryOptions({
queryKey: ['posts', 'list', filters],
queryFn: () => fetch('/api/posts').then(r => r.json()),
})
Server Actions as mutationFn
// app/posts/actions.ts
'use server'
export async function createPost(data: { title: string; body: string }) {
const post = await db.post.create({ data })
revalidatePath('/posts')
return post
}
// usage in Client Component
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts', 'list'] }),
})
Optimistic Updates
const mutation = useMutation({
mutationFn: updatePost,
onMutate: async (updated) => {
await queryClient.cancelQueries({ queryKey: ['posts', 'detail', updated.id] })
const previous = queryClient.getQueryData(['posts', 'detail', updated.id])
queryClient.setQueryData(['posts', 'detail', updated.id], (old: Post) => ({ ...old, ...updated }))
return { previous }
},
onError: (_, updated, ctx) => {
queryClient.setQueryData(['posts', 'detail', updated.id], ctx?.previous)
},
onSettled: (_, __, updated) => {
queryClient.invalidateQueries({ queryKey: ['posts', 'detail', updated.id] })
},
})
Key Rules
- Create a new
QueryClientper request in Server Components — never reuse across requests - Create one
QueryClientper browser session viauseStatein the provider - Always wrap server-prefetched subtrees in
HydrationBoundary - Mark all components using TanStack Query hooks with
'use client' - Never call
fetchdirectly in Client Components — always go throughqueryFn
Related rules
Next.js App Router + TanStack Query v5 integration with HydrationBoundary, Server Actions, and optimistic updates.
Next.js and TypeScript setup for Autonomys network block explorer development.
Next.js + TypeScript development rules with shadcn/ui, Tailwind, and Python LLM integration.
Next.js + TypeScript + Tailwind CSS setup for Autonomys block explorer development.
Next.js + Vercel + Supabase stack guide for BA Copilot, a BPMN diagram assistant for business analysts.
Next.js + TypeScript + Vercel development with AI SDK RSC, middleware, and KV database integration.
