Home / Cursor rules / Nextjs Tanstack Query

Nextjs Tanstack Query

Next.js App Router combined with TanStack Query v5 — HydrationBoundary pattern, Server Actions as mutations, optimistic updates, and infinite scroll

Cursor rules (.mdc)Public domain (CC0)
–unranked

Install

mkdir -p .cursor/rules && curl -sL https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/b044f956f021b6e8877f16781bcfc466a6a120e9/rules/nextjs-tanstack-query.mdc -o .cursor/rules/nextjs-tanstack-query.mdc

Downloads the file at commit b044f956f021 into .cursor/rules/.

To remove it later:

rm .cursor/rules/nextjs-tanstack-query.mdc

Context cost: ≈883 tokens when used.

Rules file

---
description: "Next.js App Router combined with TanStack Query v5 — HydrationBoundary pattern, Server Actions as mutations, optimistic updates, and infinite scroll"
globs: ["app/**/*.tsx", "app/**/*.ts", "src/app/**/*.tsx", "src/queries/**/*"]
alwaysApply: false
---
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
```tsx
// 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
```tsx
// 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
```ts
export const postsQueryOptions = (filters?: PostFilters) =>
  queryOptions({
    queryKey: ['posts', 'list', filters],
    queryFn: () => fetch('/api/posts').then(r => r.json()),
  })
```

## Server Actions as mutationFn
```tsx
// 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
```tsx
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** `QueryClient` per request in Server Components — never reuse across requests
- Create **one** `QueryClient` per browser session via `useState` in the provider
- Always wrap server-prefetched subtrees in `HydrationBoundary`
- Mark all components using TanStack Query hooks with `'use client'`
- Never call `fetch` directly in Client Components — always go through `queryFn`

Security

Scanned Nov 4, 2026 at commit b044f956f021, with Cisco's open-source skill-scanner.

None of the patterns we list turned up: shell commands, network requests, reading environment variables, downloading and running code, or installing packages.

A scan lists patterns in the code. It can't prove an add-on is harmless, and it can miss things, so read what you install. How we scan