SKILL.md
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
Exploreagent - Cache strategy review: Use
Taskagent - Code review: Delegate to
code-revieweragent
If the
tanstack-routerskill is available, delegate route loader and preloading patterns to it.
If thetanstack-formskill is available, delegate form submission and mutation coordination to it.
If thetanstack-tableskill is available, delegate server-side table patterns to it.
If thetanstack-startskill is available, delegate server functions and SSR data loading to it.
If thetanstack-devtoolsskill is available, delegate query cache debugging and inspection to it.
If thetanstack-dbskill is available, delegate reactive client-side database and live query patterns to it.
If thetanstack-virtualskill is available, delegate list virtualization and infinite scroll rendering to it.
If thetanstack-storeskill is available, delegate shared client-side reactive state management to it.
If theelectricsqlskill is available, delegate ElectricSQL real-time Postgres sync patterns to it.
If thelocal-firstskill is available, delegate local-first architecture decisions and sync engine selection to it.
References
- [Basic patterns, architecture, and query variants](references/basic-patterns.md)
- [Query keys and factory patterns](references/query-keys.md)
- [Mutations, optimistic updates, and MutationCache](references/mutations.md)
- [Cache operations, staleTime vs gcTime, seeding](references/caching.md)
- [Data transformations and select patterns](references/data-transformations.md)
- [Performance optimization with render tracking and structural sharing](references/performance.md)
- [Error handling strategies](references/error-handling.md)
- [Infinite queries and pagination](references/infinite-queries.md)
- [Offline mode and persistence](references/offline-mode.md)
- [WebSocket and real-time integration](references/websocket-integration.md)
- [SSR and hydration patterns](references/ssr-hydration.md)
- [TypeScript patterns](references/typescript-patterns.md)
- [Testing with MSW and React Testing Library](references/testing.md)
- [Known v5 issues and workarounds](references/known-issues.md)
- [Caching coordination with Router](references/caching-coordination.md) — single-source caching strategy, disabling Router cache, coordinated configuration