TanStack Query
Overview
TanStack Query is an async state manager, not a data fetching library. You provide a queryFn that returns a Promise; React Query handles caching, deduplication, background updates, and stale data management.
When to use: Infinite scrolling, offline-first apps, auto-refetching on focus/reconnect, complex cache invalidation, React Native, hybrid server/client apps.
When NOT to use: Purely synchronous state (useState/Zustand), normalized GraphQL caching (Apollo/urql), server-components-only apps (native fetch), simple fetch-and-display (server components).
Quick Reference
| Pattern |
API |
Key Points |
| Basic query |
useQuery({ queryKey, queryFn }) |
Include params in queryKey |
| Suspense query |
useSuspenseQuery(options) |
No enabled option allowed |
| Parallel queries |
useQueries({ queries, combine }) |
Dynamic parallel fetching |
| Dependent query |
useQuery({ enabled: !!dep }) |
Wait for prerequisite data |
| Query options |
queryOptions({ queryKey, queryFn }) |
Reusable, type-safe config |
| Basic mutation |
useMutation({ mutationFn, onSuccess }) |
Invalidate on success |
| Mutation state |
useMutationState({ filters, select }) |
Cross-component mutation tracking |
| Optimistic update |
onMutate -> cancel -> snapshot -> set |
Rollback in onError |
| Infinite query |
useInfiniteQuery({ initialPageParam }) |
initialPageParam required in v5 |
| Prefetch |
queryClient.prefetchQuery(options) |
Preload on hover/intent |
| Invalidation |
queryClient.invalidateQueries({ queryKey }) |
Fuzzy-matches by default, active only |
| Cancellation |
queryFn: ({ signal }) => fetch(url, { signal }) |
Auto-cancel on key change |
| Select transform |
select: (data) => data.filter(...) |
Structural sharing preserved |
| Skip token |
queryFn: id ? () => fetch(id) : skipToken |
Type-safe conditional disabling |
| Serial mutations |
useMutation({ scope: { id } }) |
Same scope ID runs mutations in serial |
v5 Migration Quick Reference
| v4 (Removed) |
v5 (Use Instead) |
useQuery(key, fn, opts) |
useQuery({ queryKey, queryFn, ...opts }) |
cacheTime |
gcTime |
isLoading (no data) |
isPending |
keepPreviousData: true |
placeholderData: keepPreviousData |
onSuccess/onError on queries |
useEffect or mutation callbacks |
useErrorBoundary |
throwOnError |
No initialPageParam |
initialPageParam required |
Error type unknown |
Error type defaults to Error |
Common Mistakes
| Mistake |
Correct Pattern |
Checking isPending before data |
Data-first: check data -> error -> isPending |
| Copying server state to local useState |
Use data directly or derived state pattern |
| Creating QueryClient in component |
Create once outside component or in useState |
Using refetch() for parameter changes |
Include params in queryKey, let it refetch automatically |
| Same key for useQuery and useInfiniteQuery |
Use distinct key segments (different cache structures) |
| Inline select without memoization |
Extract to stable function or useCallback |
Using catch without re-throwing |
Throw errors in queryFn (fetch doesn't reject on 4xx/5xx) |
| Manual generics on useQuery |
Type the queryFn return, let inference work |
| Destructuring query for type narrowing |
Keep query object intact for proper narrowing |
Using enabled with useSuspenseQuery |
Use conditional rendering to mount/unmount component |
| Not awaiting prefetch for SSR |
Await prefetchQuery to avoid hydration mismatches |
invalidateQueries not refetching all |
Use refetchType: 'all' for inactive queries |
Delegation
- Query pattern discovery: Use
Explore agent
- Cache strategy review: Use
Task agent
- Code review: Delegate to
code-reviewer agent
If the tanstack-router skill is available, delegate route loader and preloading patterns to it.
If the tanstack-form skill is available, delegate form submission and mutation coordination to it.
If the tanstack-table skill is available, delegate server-side table patterns to it.
If the tanstack-start skill is available, delegate server functions and SSR data loading to it.
If the tanstack-devtools skill is available, delegate query cache debugging and inspection to it.
If the tanstack-db skill is available, delegate reactive client-side database and live query patterns to it.
If the tanstack-virtual skill is available, delegate list virtualization and infinite scroll rendering to it.
If the tanstack-store skill is available, delegate shared client-side reactive state management to it.
If the electricsql skill is available, delegate ElectricSQL real-time Postgres sync patterns to it.
If the local-first skill is available, delegate local-first architecture decisions and sync engine selection to it.
References
- Basic patterns, architecture, and query variants
- Query keys and factory patterns
- Mutations, optimistic updates, and MutationCache
- Cache operations, staleTime vs gcTime, seeding
- Data transformations and select patterns
- Performance optimization with render tracking and structural sharing
- Error handling strategies
- Infinite queries and pagination
- Offline mode and persistence
- WebSocket and real-time integration
- SSR and hydration patterns
- TypeScript patterns
- Testing with MSW and React Testing Library
- Known v5 issues and workarounds
- Caching coordination with Router — single-source caching strategy, disabling Router cache, coordinated configuration
1---2name: tanstack-query3description: TanStack Query v5 server state management for React. Covers query/mutation patterns, v4-to-v5 migration (object syntax, gcTime, isPending, keepPreviousData), optimistic updates via useMutationState, SSR/hydration with HydrationBoundary, infinite queries, offline/PWA support, error boundaries with throwOnError, and React 19 Suspense integration. Use when building data fetching, fixing migration errors, debugging hydration mismatches, implementing caching strategies, or configuring mutations.4license: MIT5---6
7# TanStack Query
8
9## Overview
10
11TanStack Query is an **async state manager**, not a data fetching library. You provide a `queryFn` that returns a Promise; React Query handles caching, deduplication, background updates, and stale data management.
12
13**When to use:** Infinite scrolling, offline-first apps, auto-refetching on focus/reconnect, complex cache invalidation, React Native, hybrid server/client apps.
14
15**When NOT to use:** Purely synchronous state (useState/Zustand), normalized GraphQL caching (Apollo/urql), server-components-only apps (native fetch), simple fetch-and-display (server components).
16
17## Quick Reference
18
19| Pattern | API | Key Points |
20| ----------------- | ------------------------------------------------- | -------------------------------------- |
21| Basic query | `useQuery({ queryKey, queryFn })` | Include params in queryKey |
22| Suspense query | `useSuspenseQuery(options)` | No `enabled` option allowed |
23| Parallel queries | `useQueries({ queries, combine })` | Dynamic parallel fetching |
24| Dependent query | `useQuery({ enabled: !!dep })` | Wait for prerequisite data |
25| Query options | `queryOptions({ queryKey, queryFn })` | Reusable, type-safe config |
26| Basic mutation | `useMutation({ mutationFn, onSuccess })` | Invalidate on success |
27| Mutation state | `useMutationState({ filters, select })` | Cross-component mutation tracking |
28| Optimistic update | `onMutate` -> cancel -> snapshot -> set | Rollback in `onError` |
29| Infinite query | `useInfiniteQuery({ initialPageParam })` | `initialPageParam` required in v5 |
30| Prefetch | `queryClient.prefetchQuery(options)` | Preload on hover/intent |
31| Invalidation | `queryClient.invalidateQueries({ queryKey })` | Fuzzy-matches by default, active only |
32| Cancellation | `queryFn: ({ signal }) => fetch(url, { signal })` | Auto-cancel on key change |
33| Select transform | `select: (data) => data.filter(...)` | Structural sharing preserved |
34| Skip token | `queryFn: id ? () => fetch(id) : skipToken` | Type-safe conditional disabling |
35| Serial mutations | `useMutation({ scope: { id } })` | Same scope ID runs mutations in serial |
36
37## v5 Migration Quick Reference
38
39| v4 (Removed) | v5 (Use Instead) |
40| ------------------------------ | ------------------------------------------ |
41| `useQuery(key, fn, opts)` | `useQuery({ queryKey, queryFn, ...opts })` |
42| `cacheTime` | `gcTime` |
43| `isLoading` (no data) | `isPending` |
44| `keepPreviousData: true` | `placeholderData: keepPreviousData` |
45| `onSuccess/onError` on queries | `useEffect` or mutation callbacks |
46| `useErrorBoundary` | `throwOnError` |
47| No `initialPageParam` | `initialPageParam` required |
48| Error type `unknown` | Error type defaults to `Error` |
49
50## Common Mistakes
51
52| Mistake | Correct Pattern |
53| ------------------------------------------ | --------------------------------------------------------- |
54| Checking `isPending` before `data` | Data-first: check `data` -> `error` -> `isPending` |
55| Copying server state to local useState | Use data directly or derived state pattern |
56| Creating QueryClient in component | Create once outside component or in useState |
57| Using `refetch()` for parameter changes | Include params in queryKey, let it refetch automatically |
58| Same key for useQuery and useInfiniteQuery | Use distinct key segments (different cache structures) |
59| Inline select without memoization | Extract to stable function or useCallback |
60| Using `catch` without re-throwing | Throw errors in queryFn (fetch doesn't reject on 4xx/5xx) |
61| Manual generics on useQuery | Type the queryFn return, let inference work |
62| Destructuring query for type narrowing | Keep query object intact for proper narrowing |
63| Using `enabled` with `useSuspenseQuery` | Use conditional rendering to mount/unmount component |
64| Not awaiting prefetch for SSR | Await `prefetchQuery` to avoid hydration mismatches |
65| `invalidateQueries` not refetching all | Use `refetchType: 'all'` for inactive queries |
66
67## Delegation
68
69- **Query pattern discovery**: Use `Explore` agent
70- **Cache strategy review**: Use `Task` agent
71- **Code review**: Delegate to `code-reviewer` agent
72
73> If the `tanstack-router` skill is available, delegate route loader and preloading patterns to it.
74> If the `tanstack-form` skill is available, delegate form submission and mutation coordination to it.
75> If the `tanstack-table` skill is available, delegate server-side table patterns to it.
76> If the `tanstack-start` skill is available, delegate server functions and SSR data loading to it.
77> If the `tanstack-devtools` skill is available, delegate query cache debugging and inspection to it.
78> If the `tanstack-db` skill is available, delegate reactive client-side database and live query patterns to it.
79> If the `tanstack-virtual` skill is available, delegate list virtualization and infinite scroll rendering to it.
80> If the `tanstack-store` skill is available, delegate shared client-side reactive state management to it.
81> If the `electricsql` skill is available, delegate ElectricSQL real-time Postgres sync patterns to it.
82> If the `local-first` skill is available, delegate local-first architecture decisions and sync engine selection to it.
83
84## References
85
86- [Basic patterns, architecture, and query variants](references/basic-patterns.md)
87- [Query keys and factory patterns](references/query-keys.md)
88- [Mutations, optimistic updates, and MutationCache](references/mutations.md)
89- [Cache operations, staleTime vs gcTime, seeding](references/caching.md)
90- [Data transformations and select patterns](references/data-transformations.md)
91- [Performance optimization with render tracking and structural sharing](references/performance.md)
92- [Error handling strategies](references/error-handling.md)
93- [Infinite queries and pagination](references/infinite-queries.md)
94- [Offline mode and persistence](references/offline-mode.md)
95- [WebSocket and real-time integration](references/websocket-integration.md)
96- [SSR and hydration patterns](references/ssr-hydration.md)
97- [TypeScript patterns](references/typescript-patterns.md)
98- [Testing with MSW and React Testing Library](references/testing.md)
99- [Known v5 issues and workarounds](references/known-issues.md)
100- [Caching coordination with Router](references/caching-coordination.md) — single-source caching strategy, disabling Router cache, coordinated configuration