feat: added Next.js TanStack Query cursorrules#251
feat: added Next.js TanStack Query cursorrules#251usm4nhafeez wants to merge 1 commit intoPatrickJS:mainfrom
Conversation
📝 WalkthroughWalkthroughThis pull request introduces comprehensive documentation and cursor rules for integrating Next.js App Router with TanStack Query v5. It includes a new README entry, Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
rules-new/nextjs-tanstack-query.mdc (1)
16-30: Make code examples import-complete for copy/paste reliability.Both snippets use symbols that are never imported (
useState,QueryClient,QueryClientProvider,ReactQueryDevtools,HydrationBoundary,dehydrate,useQuery). Please include imports in each example block to avoid broken generated output.Suggested patch
## Provider Setup ```tsx // providers/query-provider.tsx 'use client' +import { useState } from 'react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' export function QueryProvider({ children }: { children: React.ReactNode }) { const [queryClient] = useState(() => new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000 } }, @@ ## Server Prefetch + HydrationBoundary Pattern ```tsx // app/posts/page.tsx (Server Component) +import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query' +import { postsQueryOptions } from '@/queries/posts' +import { PostsList } from './_components/posts-list' export default async function PostsPage() { @@ // app/posts/_components/posts-list.tsx (Client Component) 'use client' +import { useQuery } from '@tanstack/react-query' +import { postsQueryOptions } from '@/queries/posts' export function PostsList() {Also applies to: 34-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rules-new/nextjs-tanstack-query.mdc` around lines 16 - 30, The examples are missing necessary imports which break copy/paste; add imports for React's useState and all tanstack symbols used in the snippets (useState, QueryClient, QueryClientProvider, ReactQueryDevtools in providers/query-provider.tsx, and HydrationBoundary, QueryClient, dehydrate in the server example, plus useQuery in the client PostsList) so each code block is import-complete—update the top of each example to import those exact identifiers from 'react' and '@tanstack/react-query' / '@tanstack/react-query-devtools' as appropriate and keep existing local imports like postsQueryOptions and PostsList unchanged.rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc (1)
16-30: Replicate import-complete snippets here as well.This file has the same missing-import issue in example code; please add explicit imports so generated code is immediately runnable.
Also applies to: 34-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc` around lines 16 - 30, The example QueryProvider is missing explicit imports, so add the necessary imports at the top of the file for React, useState, QueryClient, QueryClientProvider and ReactQueryDevtools (used by the QueryProvider function) so the snippet is runnable; update the snippet that defines QueryProvider to include import statements for React and useState from 'react', QueryClient and QueryClientProvider from '@tanstack/react-query', and ReactQueryDevtools from '@tanstack/react-query-devtools'.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@rules-new/nextjs-tanstack-query.mdc`:
- Around line 16-30: The examples are missing necessary imports which break
copy/paste; add imports for React's useState and all tanstack symbols used in
the snippets (useState, QueryClient, QueryClientProvider, ReactQueryDevtools in
providers/query-provider.tsx, and HydrationBoundary, QueryClient, dehydrate in
the server example, plus useQuery in the client PostsList) so each code block is
import-complete—update the top of each example to import those exact identifiers
from 'react' and '@tanstack/react-query' / '@tanstack/react-query-devtools' as
appropriate and keep existing local imports like postsQueryOptions and PostsList
unchanged.
In
`@rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc`:
- Around line 16-30: The example QueryProvider is missing explicit imports, so
add the necessary imports at the top of the file for React, useState,
QueryClient, QueryClientProvider and ReactQueryDevtools (used by the
QueryProvider function) so the snippet is runnable; update the snippet that
defines QueryProvider to include import statements for React and useState from
'react', QueryClient and QueryClientProvider from '@tanstack/react-query', and
ReactQueryDevtools from '@tanstack/react-query-devtools'.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 29a6543f-daac-4dd8-b1b1-1831d3593148
📒 Files selected for processing (5)
README.mdrules-new/nextjs-tanstack-query.mdcrules/nextjs-tanstack-query-cursorrules-prompt-file/.cursorrulesrules/nextjs-tanstack-query-cursorrules-prompt-file/README.mdrules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc
There was a problem hiding this comment.
Pull request overview
Adds a new Cursor ruleset for using TanStack Query v5 in a Next.js App Router codebase, focusing on server→client hydration, Server Actions as mutations, and optimistic update patterns.
Changes:
- Introduces a new
rules/nextjs-tanstack-query-cursorrules-prompt-file/rules package with.cursorrules, an.mdcrule file, and a README. - Adds a corresponding
rules-new/nextjs-tanstack-query.mdcentry. - Links the new ruleset from the repository root
README.md.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| rules/nextjs-tanstack-query-cursorrules-prompt-file/README.md | Documents what the new ruleset covers and provides attribution. |
| rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc | Adds an .mdc rule describing Next.js + TanStack Query v5 patterns and examples. |
| rules/nextjs-tanstack-query-cursorrules-prompt-file/.cursorrules | Adds the main .cursorrules guidance and code examples (provider, hydration, mutations, optimistic updates, infinite queries). |
| rules-new/nextjs-tanstack-query.mdc | Adds a rules-new version of the same .mdc rule content. |
| README.md | Adds the new ruleset to the public list of available rules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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( | ||
| () => |
There was a problem hiding this comment.
In the Provider snippet, children is typed as React.ReactNode but React isn’t imported. In many TS/Next.js setups this will fail with “Cannot find namespace 'React'”. Prefer import type { PropsWithChildren } from 'react' (or ReactNode) and type the props accordingly, or add an explicit React type import in the example.
| defaultOptions: { | ||
| queries: { | ||
| staleTime: 60 * 1000, | ||
| retry: (count, error: any) => error?.status !== 404 && count < 2, |
There was a problem hiding this comment.
retry: (count, error) => error?.status !== 404 ... assumes the thrown error has an HTTP status, but the fetch examples shown (fetch(...).then(r => r.json())) won’t throw on non-2xx responses and typically won’t provide status. Either adjust the example fetchers to throw with status when !r.ok, or change the retry predicate to match the actual error type you expect.
| retry: (count, error: any) => error?.status !== 404 && count < 2, | |
| retry: (count) => count < 2, |
| 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 })) |
There was a problem hiding this comment.
The optimistic update uses queryClient.setQueryData(..., (old: Post) => ({ ...old, ...updated })). At runtime old can be undefined (e.g., if the detail query was never fetched), and spreading undefined will throw. Update the example to safely handle undefined (return old/initialize a default) and avoid typing old as always-present.
| queryClient.setQueryData(postKeys.detail(updated.id), (old: Post) => ({ ...old, ...updated })) | |
| queryClient.setQueryData(postKeys.detail(updated.id), (old: Post | undefined) => | |
| old ? { ...old, ...updated } : old, | |
| ) |
| @@ -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/**/*"] | |||
There was a problem hiding this comment.
Frontmatter globs is formatted as a YAML array, but other .mdc rules in this repo use a single glob string (e.g. rules/nextjs-react-typescript-cursorrules-prompt-file/next-js-server-actions.mdc:3). If a tool parses these files assuming the string format, this entry may not be applied. Also consider including provider paths (e.g. src/providers/**/* / providers/**/*) since the guidance includes QueryProvider setup.
| globs: ["app/**/*.tsx", "app/**/*.ts", "src/app/**/*.tsx", "src/queries/**/*"] | |
| globs: "{app/**/*.tsx,app/**/*.ts,src/app/**/*.tsx,src/queries/**/*,providers/**/*,src/providers/**/*}" |
| 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 })) |
There was a problem hiding this comment.
Same optimistic update issue as in the .cursorrules file: setQueryData(..., (old: Post) => ({ ...old, ...updated })) will throw if old is undefined. The example should defensively handle missing cached data and avoid asserting old is always a Post.
| queryClient.setQueryData(['posts', 'detail', updated.id], (old: Post) => ({ ...old, ...updated })) | |
| queryClient.setQueryData( | |
| ['posts', 'detail', updated.id], | |
| (old: Post | undefined) => (old ? { ...old, ...updated } : updated), | |
| ) |
| 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 })) |
There was a problem hiding this comment.
The optimistic update example spreads old ({ ...old, ...updated }) but old can be undefined if the cache is empty; spreading undefined throws. Update the example to guard against undefined and avoid typing old as always-present.
| queryClient.setQueryData(['posts', 'detail', updated.id], (old: Post) => ({ ...old, ...updated })) | |
| queryClient.setQueryData( | |
| ['posts', 'detail', updated.id], | |
| (old: Post | undefined) => (old ? { ...old, ...updated } : updated) | |
| ) |
| globs: ["app/**/*.tsx", "app/**/*.ts", "src/app/**/*.tsx", "src/queries/**/*"] | ||
| alwaysApply: false | ||
| --- | ||
|
|
There was a problem hiding this comment.
This file appears to be a near-exact duplicate of rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc, which risks the two drifting over time—consider removing one or documenting which is the source of truth. Also, globs is expressed as a YAML array here, while most rules-new/*.mdc use a single comma-separated glob string (e.g. rules-new/react.mdc:3), so this entry may not be picked up by tooling expecting the common format.
| globs: ["app/**/*.tsx", "app/**/*.ts", "src/app/**/*.tsx", "src/queries/**/*"] | |
| alwaysApply: false | |
| --- | |
| globs: "app/**/*.tsx, app/**/*.ts, src/app/**/*.tsx, src/queries/**/*" | |
| alwaysApply: false | |
| --- | |
| Note: This file mirrors `rules/nextjs-tanstack-query-cursorrules-prompt-file/nextjs-tanstack-query.mdc`, which should be treated as the source of truth. Keep the two files synchronized if either is updated. |
Added rules for using TanStack Query v5 inside Next.js App Router. Covers HydrationBoundary pattern, Server Actions as mutations, and optimistic updates. No existing entry covers this combination.
Summary by CodeRabbit