home / skills / gilbertopsantosjr / fullstacknextjs / tanstack-react-query
/skills/tanstack-react-query
This skill provides expert guidance on TanStack React Query patterns for data fetching, mutations, caching, and server action integration in React apps.
npx playbooks add skill gilbertopsantosjr/fullstacknextjs --skill tanstack-react-queryReview the files below or copy the command above to add this skill to your agents.
---
name: tanstack-react-query
description: TanStack React Query expert for data fetching and mutations in React applications. Use when working with useQuery, useMutation, cache invalidation, optimistic updates, query keys, or integrating server actions with React Query via @saas4dev/core hooks (useServerActionQuery, useServerActionMutation, useServerActionInfiniteQuery). Triggers on requests involving API data fetching, server state management, cache strategies, or converting fetch/useEffect patterns to React Query.
---
# TanStack React Query Expert
Expert guidance for idiomatic React Query (TanStack Query v5) patterns in React applications, with special focus on ZSA server action integration via `@saas4dev/core`.
## Core Hooks
### From @saas4dev/core (Server Actions)
```typescript
import {
useServerActionQuery,
useServerActionMutation,
useServerActionInfiniteQuery,
} from '@saas4dev/core'
```
### From @tanstack/react-query (Direct API)
```typescript
import {
useQuery,
useMutation,
useInfiniteQuery,
useQueryClient,
} from '@tanstack/react-query'
```
## Decision Tree
```
Need to fetch data?
├── From server action → useServerActionQuery
├── From REST/fetch directly → useQuery
└── Paginated/infinite → useServerActionInfiniteQuery or useInfiniteQuery
Need to modify data?
├── From server action → useServerActionMutation
└── From REST/fetch directly → useMutation
After mutation, what cache behavior?
├── Simple: just invalidate → queryClient.invalidateQueries()
├── Update specific item → queryClient.setQueryData()
└── Need instant feedback → Optimistic update pattern
```
## Quick Patterns
### Server Action Query
```typescript
const { data, isLoading } = useServerActionQuery(listUsersAction, {
input: { status: 'active' },
queryKey: ['users', 'list', { status: 'active' }],
})
```
### Server Action Mutation with Invalidation
```typescript
const queryClient = useQueryClient()
const mutation = useServerActionMutation(createUserAction, {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] })
toast.success('User created')
},
onError: (error) => toast.error(error.message),
})
```
### Optimistic Update
```typescript
const mutation = useServerActionMutation(updateTodoAction, {
onMutate: async (newData) => {
await queryClient.cancelQueries({ queryKey: ['todos', newData.id] })
const previous = queryClient.getQueryData(['todos', newData.id])
queryClient.setQueryData(['todos', newData.id], (old) => ({ ...old, ...newData }))
return { previous }
},
onError: (err, newData, context) => {
queryClient.setQueryData(['todos', newData.id], context?.previous)
},
onSettled: (data, err, variables) => {
queryClient.invalidateQueries({ queryKey: ['todos', variables.id] })
},
})
```
## Query Key Structure
### Hierarchy Pattern
```typescript
['entity'] // All of entity
['entity', 'list'] // All lists
['entity', 'list', { filters }] // Filtered list
['entity', 'detail', id] // Single item
['entity', id, 'nested'] // Nested resource
```
### Query Key Factory
```typescript
export const userKeys = {
all: ['users'] as const,
lists: () => [...userKeys.all, 'list'] as const,
list: (filters: Filters) => [...userKeys.lists(), filters] as const,
details: () => [...userKeys.all, 'detail'] as const,
detail: (id: string) => [...userKeys.details(), id] as const,
}
```
## Configuration Defaults
```typescript
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
gcTime: 5 * 60 * 1000, // 5 minutes
retry: 1,
refetchOnWindowFocus: false,
},
},
})
```
## Best Practices
1. **Query Keys**: Use hierarchical keys; invalidate broadly, fetch specifically
2. **Mutations**: Always invalidate or update related queries on success
3. **Loading States**: Use `isLoading` for first load, `isFetching` for background updates
4. **Error Handling**: Handle in `onError` callback; show user-friendly messages
5. **Optimistic Updates**: Use for high-confidence mutations; always implement rollback
6. **Conditional Queries**: Use `enabled` option, not conditional hook calls
7. **Derived Data**: Use `select` to transform data; keeps original in cache
## References
Detailed patterns and examples:
- **[query-patterns.md](references/query-patterns.md)**: Query keys, useQuery, pagination, parallel/dependent queries
- **[mutation-patterns.md](references/mutation-patterns.md)**: Mutations, cache invalidation, optimistic updates, rollback
- **[advanced-patterns.md](references/advanced-patterns.md)**: Custom hooks, prefetching, SSR hydration, testing
## Skill Interface
When using this skill, provide:
```json
{
"apiDescription": "REST/GraphQL endpoints, methods, parameters, response shapes",
"uiScenario": "What the UI needs (e.g., 'List with pagination', 'Edit form with instant feedback')",
"constraints": "React Query v5, fetch vs axios, suspense vs traditional",
"currentCode": "(optional) Existing code to improve"
}
```
Response includes:
- **recommendations**: Query keys, hooks, invalidation strategy
- **exampleCode**: React components/hooks demonstrating patterns
- **notes**: Why these patterns were chosen
This skill is a TanStack React Query expert focused on idiomatic data fetching and mutations in React apps, including integration with @saas4dev/core server action hooks. It provides concrete recommendations for query keys, cache strategies, optimistic updates, and when to use useQuery/useMutation vs server-action variants. Use it to convert fetch/useEffect patterns into resilient, cache-aware React Query usage.
The skill inspects the API surface, UI requirements, and current code to recommend query key shapes, which hooks to use (useQuery, useMutation, useInfiniteQuery or server-action equivalents), and the appropriate cache behavior after mutations. It returns actionable recommendations: query key factories, default QueryClient options, invalidation/update strategies, and example code for optimistic updates and paginated lists. It also highlights error handling, loading-state distinctions, and conditional query usage.
When should I prefer server-action hooks over useQuery/useMutation?
Use server-action hooks (useServerActionQuery/useServerActionMutation) when your backend exposes server actions and you want tight integration, simpler serialization, and consistent behavior. Use useQuery/useMutation for direct REST or GraphQL fetches.
How do I choose between invalidation and setQueryData after a mutation?
Invalidate when the source of truth may change broadly or on complex server-side logic. Use setQueryData to update a specific cache entry for immediate UI consistency, then optionally invalidate onSettled to reconcile with server state.