nextjs tanstack query
via PatrickJS/awesome-cursorrules
Next.js App Router + TanStack Query v5 with server prefetch, Server Actions mutations, and optimistic updates
What is nextjs tanstack query?
Combines Next.js App Router Server Components with TanStack Query v5 for efficient data fetching and mutations. Use this rule when building interactive Next.js apps that need server-side initial data loading, client-side mutations via Server Actions, optimistic UI updates, and infinite scroll patterns.
- Prefetch data on the server and hydrate the Query cache via HydrationBoundary to eliminate client waterfalls
- Use Server Actions as mutation functions with automatic cache invalidation and revalidation
- Implement optimistic updates with rollback on error using onMutate, onError, and onSettled hooks
- Manage separate QueryClient instances per request (server) and per session (client) to prevent data leaks
- Structure queryOptions factories for reusable, type-safe query definitions across Server and Client Components
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
Senior full-stack TypeScript, React, Node.js guidance with clean architecture, testing, and WHY-oriented reasoning.
Quantitative factor research skills for designing, evaluating, and mining alpha factors in equities markets.
Android development with Jetpack Compose, clean architecture, and Material Design 3.
Angular development with Novo Elements UI library using standalone components.
Expert Angular 18 + TypeScript development with Jest, emphasizing clean code and performance.
Manage Kubernetes clusters, add-ons, stacks, and credentials via the Ankra CLI platform.