TanStack Query v5 Rules
## what it does
tl;dr: pull tanstack-query-v5-cursorrules-prompt-file.mdc from awesome-cursorrules before using TanStack Query v5 in a Cursor project. Models drift toward Redux/Zustand for server state and break the cache.
What it does
This rule set configures Cursor to use TanStack Query v5 the way it was designed: server state only, structured query keys, the queryOptions helper for shared definitions, and rollback-safe optimistic updates. Without it, models mix client state into the cache and use the v4 positional useQuery signature.
Sourced from PatrickJS/awesome-cursorrules — the community registry for .mdc Project Rules.
The rules
# TanStack Query v5 Guidelines
## Core Philosophy
- TanStack Query manages server state — it is NOT a general state manager for client-only state
- Every query should have a stable, serializable query key that uniquely describes the data
- Mutations handle writes; queries handle reads — never blur this boundary
- Prefer `queryOptions()` helper for reusable, co-located query definitions
- v5 breaking changes: `useQuery` no longer accepts positional args; always use the options object form
## Query Keys
- Always structure keys as arrays: `['entity', 'list']`, `['entity', 'detail', id]`
- Use a query key factory to avoid typos and enable easy invalidation
## queryOptions Helper (v5)
- Use `queryOptions()` to define queries once and reuse across components and loaders
- Pairs with router loaders via `queryClient.ensureQueryData(...)`
## Optimistic Updates
- Cancel in-flight queries, snapshot previous data, set new data, roll back on error, invalidate on settled
## Infinite Queries
- `getNextPageParam` returns the next cursor; `data.pages` is the array of page results
## Suspense Mode
- Use `useSuspenseQuery` (v5) paired with `<Suspense>` and `<ErrorBoundary>`
- No `isLoading` handling needed — Suspense handles it
## Performance Best Practices
- Set appropriate `staleTime` per query — defaults to `0` (always stale)
- Use `select` to subscribe to only relevant slices
- `placeholderData: keepPreviousData` for pagination
- Instantiate `QueryClient` once at app root, never inside components
## Error Handling
- `throwOnError: true` to bubble to nearest ErrorBoundary
- Conditional `retry` (e.g., skip 404)
Installation
Drop the .mdc into .cursor/rules/ of your project:
mkdir -p .cursor/rules
curl -sL \
https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/main/rules/tanstack-query-v5-cursorrules-prompt-file.mdc \
-o .cursor/rules/tanstack-query-v5.mdc
Cursor picks up files in .cursor/rules/ automatically.
Setup snippet
// main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60, // 1 minute default
retry: 2,
refetchOnWindowFocus: true,
},
},
})
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Query key factory
// queryKeys.ts
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,
}
Why it helps
Without v5-aware rules, models will:
- Pass positional args to
useQuery(v4 signature — fails in v5) - Use Redux or Zustand for server state, duplicating the cache
- Skip
queryOptionsand inline query definitions everywhere - Skip the cancel/snapshot/set/rollback/invalidate sequence on optimistic updates
- Create a new
QueryClientper component, breaking the cache
Gotchas
- These are Cursor
.mdcrules, not a Claude CodeCLAUDE.mdfile. - v5 broke several v4 patterns. Models trained on older code default to v4.
useSuspenseQueryrequires both a<Suspense>boundary and an<ErrorBoundary>.
Related
- React Router v7 Rules — loaders can call
queryClient.ensureQueryData(...)to eliminate loading spinners - Next.js App Router Rules — TanStack Query pairs cleanly with RSC server prefetch
## embed this badge
