TanStack Query
Pathrule2 Rules • 2 Memories • 1 Skill
A guardrail bundle for TanStack Query v5 in React apps. It enforces structured query key factories, deliberate staleTime, and mutation-driven invalidation so server state stays consistent. The rules keep data fetching out of effects and push every query through reusable queryOptions.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
/ workspace root
tanstack-query-review
src/
Never fetch in useEffect for server state
Invalidate related queries after every mutation
TanStack Query v5 defaults and staleTime tuning
api/
Query key factories are the cache contract
Rules
2Never fetch in useEffect for server state/srchighstrictUse useQuery instead of useEffect plus useState for any server data.
| 1 | Server state belongs to TanStack Query, not to component effects. Fetching in `useEffect` and storing the result in `useState` reintroduces every bug the library exists to solve: no caching, no dedupe, manual loading and error flags, and race conditions on fast navigation. |
| 2 | |
| 3 | - Replace any `useEffect` that calls `fetch` or an API client with `useQuery` or `useSuspenseQuery`. |
| 4 | - Read status from `query.status`, `isPending`, and `isError` instead of hand-rolled `useState` flags. |
| 5 | - Use `enabled` (or component composition with `useSuspenseQuery`) for dependent queries rather than gating fetches inside an effect. |
| 6 | - Reserve `useEffect` for true side effects like subscriptions and DOM work, never for data loading. |
Memories
2Query key factories are the cache contract/src/apiCentralize hierarchical query keys per entity so invalidation is predictable.
| 1 | Query keys are how TanStack Query identifies, dedupes, and invalidates cache entries, so they live in one factory per entity rather than as scattered inline arrays. |
| 2 | |
| 3 | - Shape each factory hierarchically: `all`, `lists()`, `list(filters)`, `details()`, `detail(id)` so a single `invalidateQueries({ queryKey: keys.all })` can clear a whole entity. |
| 4 | - Include every input a query depends on (filters, ids, pagination) in the key; changing the key is how a new fetch is triggered. |
| 5 | - Pair factories with `queryOptions()` from v5 so the same key, `queryFn`, and `staleTime` are reused across `useQuery`, `useSuspenseQuery`, `prefetchQuery`, and `setQueryData`. |
| 6 | - Keys must be serializable and order-stable; objects are compared by deep structure, not reference. |
TanStack Query v5 defaults and staleTime tuning/srcKnow the v5 defaults before overriding refetch and cache behavior.
| 1 | TanStack Query v5 (`@tanstack/react-query` 5.x) ships aggressive freshness defaults that surprise teams expecting cache-first behavior. |
| 2 | |
| 3 | - `staleTime` defaults to `0`, so data is stale immediately and refetches on mount, window focus, and reconnect; set a realistic value like `5 * 60 * 1000` per query for data that does not change every second. |
| 4 | - `gcTime` (renamed from `cacheTime`) defaults to 5 minutes and controls when inactive cache entries are garbage collected, not staleness. |
| 5 | - Use `staleTime: Infinity` for data that only changes via your own mutations, and `staleTime: 'static'` for truly immutable data like feature flags. |
| 6 | - v5 removed the `suspense: true` flag on `useQuery`; use the dedicated `useSuspenseQuery`, and note it has no `enabled` option since loading is handled by Suspense and errors by an Error Boundary. |
Skills
1tanstack-query-review/rootChecklist for reviewing TanStack Query v5 usage before merge.
| 1 | --- |
| 2 | name: tanstack-query-review |
| 3 | description: Review checklist for TanStack Query v5 code. Use when adding or changing useQuery, useSuspenseQuery, useMutation, query keys, or QueryClient config in a React or Next.js codebase. |
| 4 | --- |
| 5 | |
| 6 | # TanStack Query review |
| 7 | |
| 8 | - [ ] No server data is fetched in `useEffect` plus `useState`; every read uses `useQuery` or `useSuspenseQuery` |
| 9 | - [ ] Query keys come from a centralized factory, not inline ad-hoc arrays, and include every input the query depends on |
| 10 | - [ ] Shared queries are defined with `queryOptions()` and reused across hooks, prefetch, and `setQueryData` |
| 11 | - [ ] `staleTime` and `gcTime` are set deliberately rather than relying on the `0ms` stale default |
| 12 | - [ ] Every `useMutation` invalidates or updates the queries its write affects in `onSuccess` or `onSettled` |
| 13 | - [ ] Optimistic updates cancel in-flight queries, snapshot previous data, and roll back in `onError` |
| 14 | - [ ] Loading and error states read from `isPending`, `isError`, and Error Boundaries, not hand-rolled flags |
| 15 | - [ ] `enabled` or component composition handles dependent queries; nothing gates fetching inside an effect |
Why this pattern
Server state drifts out of sync because queries use ad-hoc keys, fetch inside effects, and never invalidate after mutations.
Built for React and Next.js teams using TanStack Query v5 for server state.
Keeps your assistant from:
- Fetching data inside useEffect and storing it in component state instead of using useQuery
- Ad-hoc string query keys that make invalidation unpredictable and silently serve stale data
- Mutations that update the server but never invalidate or update the related query cache
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-06-09