diff --git a/README.md b/README.md
index 63c6a2ff..6aab5892 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@ By creating a `.cursorrules` file in your project's root directory, you can leve
- [Next.js (React, TypeScript)](./rules/nextjs-react-typescript-cursorrules-prompt-file/.cursorrules) - Cursor rules for Next.js development with React and TypeScript integration.
- [Next.js (SEO Development)](./rules/nextjs-seo-dev-cursorrules-prompt-file/.cursorrules) - Cursor rules for Next.js development with SEO optimization.
- [Next.js (Supabase Todo App)](./rules/nextjs-supabase-todo-app-cursorrules-prompt-file/.cursorrules) - Cursor rules for Next.js development with Supabase integration for a Todo app.
+- [Next.js (TanStack Query v5)](./rules/nextjs-tanstack-query-cursorrules-prompt-file/.cursorrules) - Cursor rules for Next.js App Router with TanStack Query v5, covering the HydrationBoundary pattern, Server Actions as mutations, and optimistic updates.
- [Next.js (Tailwind, TypeScript)](./rules/nextjs-tailwind-typescript-apps-cursorrules-prompt/.cursorrules) - Cursor rules for Next.js development with Tailwind CSS and TypeScript integration.
- [Next.js (TypeScript App)](./rules/nextjs-typescript-app-cursorrules-prompt-file/.cursorrules) - Cursor rules for Next.js development with TypeScript integration.
- [Next.js (TypeScript)](./rules/nextjs-typescript-cursorrules-prompt-file/.cursorrules) - Cursor rules for Next.js development with TypeScript integration.
diff --git a/rules-new/nextjs-tanstack-query.mdc b/rules-new/nextjs-tanstack-query.mdc
new file mode 100644
index 00000000..b8251c3b
--- /dev/null
+++ b/rules-new/nextjs-tanstack-query.mdc
@@ -0,0 +1,103 @@
+---
+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 (
+
+ {children}
+
+
+ )
+}
+```
+
+## 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 (
+
+
+
+ )
+}
+
+// app/posts/_components/posts-list.tsx (Client Component)
+'use client'
+export function PostsList() {
+ const { data: posts } = useQuery(postsQueryOptions()) // reads from pre-populated cache
+ return
{posts?.map(p =>
{p.title}
)}
+}
+```
+
+## 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`
diff --git a/rules/nextjs-tanstack-query-cursorrules-prompt-file/.cursorrules b/rules/nextjs-tanstack-query-cursorrules-prompt-file/.cursorrules
new file mode 100644
index 00000000..5d231acb
--- /dev/null
+++ b/rules/nextjs-tanstack-query-cursorrules-prompt-file/.cursorrules
@@ -0,0 +1,227 @@
+You are an expert in Next.js (App Router), TanStack Query v5, TypeScript, and combining server components with client-side data fetching.
+
+# Next.js App Router + TanStack Query v5 Guidelines
+
+## Architecture Philosophy
+- Server Components fetch data directly (no TanStack Query needed there)
+- TanStack Query lives in Client Components for interactive, real-time, or user-triggered data
+- Use React Server Components for initial page data; TanStack Query for mutations, polling, and optimistic updates
+- Hydrate the Query cache from server to avoid client waterfalls on first load
+
+## Provider Setup with Hydration
+```tsx
+// src/providers/query-provider.tsx
+'use client'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
+import { useState } from 'react'
+
+export function QueryProvider({ children }: { children: React.ReactNode }) {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 60 * 1000,
+ retry: (count, error: any) => error?.status !== 404 && count < 2,
+ },
+ },
+ }),
+ )
+
+ return (
+
+ {children}
+
+
+ )
+}
+
+// src/app/layout.tsx
+import { QueryProvider } from '@/providers/query-provider'
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+## Hydration Pattern (Server → Client Cache)
+- Prefetch in Server Components, dehydrate state, rehydrate in client
+- This eliminates client-side loading states on first render
+```tsx
+// src/app/posts/page.tsx (Server Component)
+import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
+import { postsQueryOptions } from '@/queries/posts'
+import { PostsList } from './_components/posts-list'
+
+export default async function PostsPage() {
+ const queryClient = new QueryClient()
+ await queryClient.prefetchQuery(postsQueryOptions())
+
+ return (
+
+
+
+ )
+}
+
+// src/app/posts/_components/posts-list.tsx
+'use client'
+import { useQuery } from '@tanstack/react-query'
+import { postsQueryOptions } from '@/queries/posts'
+
+export function PostsList() {
+ // Reads from pre-populated cache — no loading spinner
+ const { data: posts } = useQuery(postsQueryOptions())
+ return
{posts?.map(p =>
{p.title}
)}
+}
+```
+
+## Query Definitions
+```ts
+// src/queries/posts.ts
+import { queryOptions } from '@tanstack/react-query'
+
+export const postKeys = {
+ all: ['posts'] as const,
+ lists: () => [...postKeys.all, 'list'] as const,
+ list: (filters?: PostFilters) => [...postKeys.lists(), { filters }] as const,
+ details: () => [...postKeys.all, 'detail'] as const,
+ detail: (id: string) => [...postKeys.details(), id] as const,
+}
+
+export const postsQueryOptions = (filters?: PostFilters) =>
+ queryOptions({
+ queryKey: postKeys.list(filters),
+ queryFn: () => fetch(`/api/posts`).then(r => r.json()),
+ })
+
+export const postDetailQueryOptions = (id: string) =>
+ queryOptions({
+ queryKey: postKeys.detail(id),
+ queryFn: () => fetch(`/api/posts/${id}`).then(r => r.json()),
+ staleTime: 1000 * 60 * 5,
+ })
+```
+
+## Server Actions + Mutations
+- Use Next.js Server Actions as the `mutationFn` in TanStack Query mutations
+- This gives you type-safe server mutations WITH optimistic update/rollback capabilities
+```tsx
+// src/app/posts/actions.ts
+'use server'
+import { revalidatePath } from 'next/cache'
+
+export async function createPost(data: { title: string; body: string }) {
+ const post = await db.post.create({ data })
+ revalidatePath('/posts')
+ return post
+}
+
+// src/app/posts/_components/create-post-form.tsx
+'use client'
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { createPost } from '../actions'
+import { postKeys } from '@/queries/posts'
+
+export function CreatePostForm() {
+ const queryClient = useQueryClient()
+ const mutation = useMutation({
+ mutationFn: createPost,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: postKeys.lists() })
+ },
+ })
+
+ return (
+
+ )
+}
+```
+
+## Optimistic Updates with Server Actions
+```tsx
+const mutation = useMutation({
+ mutationFn: updatePost,
+ onMutate: async (updated) => {
+ await queryClient.cancelQueries({ queryKey: postKeys.detail(updated.id) })
+ const previous = queryClient.getQueryData(postKeys.detail(updated.id))
+ queryClient.setQueryData(postKeys.detail(updated.id), (old: Post) => ({ ...old, ...updated }))
+ return { previous }
+ },
+ onError: (_, updated, ctx) => {
+ queryClient.setQueryData(postKeys.detail(updated.id), ctx?.previous)
+ },
+ onSettled: (_, __, updated) => {
+ queryClient.invalidateQueries({ queryKey: postKeys.detail(updated.id) })
+ },
+})
+```
+
+## When to Use Server Components vs TanStack Query
+| Use Server Components When | Use TanStack Query When |
+|---|---|
+| Static or rarely-changing data | Real-time or frequently-updated data |
+| SEO-critical initial content | User interactions (forms, toggles) |
+| No need to refetch on client | Optimistic updates needed |
+| Data is not shared across components | Data is shared across many components |
+| No loading states desired | Fine-grained loading/error UI needed |
+
+## Route Handlers (API Routes) as Query Targets
+- Use `src/app/api/` route handlers as the API layer for TanStack Query fetchers
+- Keep route handlers thin — just parse/validate input and call service layer
+```ts
+// src/app/api/posts/route.ts
+import { NextResponse } from 'next/server'
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url)
+ const posts = await getPosts({ category: searchParams.get('category') })
+ return NextResponse.json(posts)
+}
+```
+
+## Infinite Queries (Pagination / Infinite Scroll)
+```tsx
+'use client'
+import { useInfiniteQuery } from '@tanstack/react-query'
+
+export function InfinitePosts() {
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
+ queryKey: postKeys.lists(),
+ queryFn: ({ pageParam }) =>
+ fetch(`/api/posts?cursor=${pageParam ?? ''}`).then(r => r.json()),
+ initialPageParam: undefined as string | undefined,
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ })
+
+ const posts = data?.pages.flatMap(p => p.items) ?? []
+
+ return (
+
+ {posts.map(post => )}
+
+
+ )
+}
+```
+
+## Key Rules
+- Create one `QueryClient` per request on the server side (inside Server Components)
+- Create one `QueryClient` per browser session on the client (via `useState` in provider)
+- Always use `HydrationBoundary` when passing server-prefetched data to client components
+- Never call `fetch` inside Client Components directly — always go through `queryFn`
+- Mark all components that use TanStack Query hooks with `'use client'`
diff --git a/rules/nextjs-tanstack-query-cursorrules-prompt-file/README.md b/rules/nextjs-tanstack-query-cursorrules-prompt-file/README.md
new file mode 100644
index 00000000..38846d9e
--- /dev/null
+++ b/rules/nextjs-tanstack-query-cursorrules-prompt-file/README.md
@@ -0,0 +1,18 @@
+# Next.js App Router + TanStack Query v5 Cursor Rules
+
+Cursor rules for combining Next.js App Router with TanStack Query v5 — covering the hydration pattern, Server Components vs Client Components data fetching strategy, Server Actions as mutation functions, and optimistic updates.
+
+## What's covered
+- QueryProvider setup with per-session `QueryClient`
+- Server-side prefetch + `HydrationBoundary` hydration pattern
+- `queryOptions` factory pattern with key factories
+- Next.js Server Actions as `mutationFn` in TanStack Query mutations
+- Optimistic updates with rollback
+- Infinite scroll with `useInfiniteQuery`
+- When to use Server Components vs TanStack Query (decision table)
+- Route Handler (API route) patterns for query targets
+
+## Author
+Created by [usm4nhafeez](https://github.com/usm4nhafeez)
+
+Contributed to [awesome-cursorrules](https://github.com/PatrickJS/awesome-cursorrules)
diff --git a/rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc b/rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc
new file mode 100644
index 00000000..b8251c3b
--- /dev/null
+++ b/rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc
@@ -0,0 +1,103 @@
+---
+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 (
+
+ {children}
+
+
+ )
+}
+```
+
+## 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 (
+
+
+
+ )
+}
+
+// app/posts/_components/posts-list.tsx (Client Component)
+'use client'
+export function PostsList() {
+ const { data: posts } = useQuery(postsQueryOptions()) // reads from pre-populated cache
+ return
{posts?.map(p =>
{p.title}
)}
+}
+```
+
+## 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`