library/specializations/web-development/skills/react-query/SKILL.md
TanStack Query (React Query) patterns for server state management, caching, mutations, optimistic updates, and infinite queries.
npx skillsauth add a5c-ai/babysitter react-queryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert assistance for implementing TanStack Query (React Query) for server state management in React applications.
Invoke this skill when you need to:
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | endpoint | string | Yes | API endpoint to query | | queryKey | array | Yes | Unique query key | | staleTime | number | No | Time until data is stale (ms) | | cacheTime | number | No | Time to keep in cache (ms) | | optimisticUpdate | boolean | No | Enable optimistic updates |
{
"endpoint": "/api/users",
"queryKey": ["users"],
"staleTime": 300000,
"cacheTime": 600000,
"optimisticUpdate": true
}
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 30, // 30 minutes (formerly cacheTime)
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
refetchOnWindowFocus: false,
},
mutations: {
retry: 1,
},
},
});
export function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
import { useQuery } from '@tanstack/react-query';
interface User {
id: string;
name: string;
email: string;
}
async function fetchUsers(): Promise<User[]> {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Failed to fetch users');
}
return response.json();
}
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
}
export function useUser(userId: string) {
return useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
enabled: !!userId,
});
}
import { useMutation, useQueryClient } from '@tanstack/react-query';
interface UpdateUserDto {
id: string;
name?: string;
email?: string;
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (data: UpdateUserDto) => {
const response = await fetch(`/api/users/${data.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) throw new Error('Failed to update user');
return response.json();
},
onMutate: async (newData) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['users', newData.id] });
// Snapshot previous value
const previousUser = queryClient.getQueryData(['users', newData.id]);
// Optimistically update
queryClient.setQueryData(['users', newData.id], (old: User) => ({
...old,
...newData,
}));
return { previousUser };
},
onError: (err, newData, context) => {
// Rollback on error
if (context?.previousUser) {
queryClient.setQueryData(['users', newData.id], context.previousUser);
}
},
onSettled: (data, error, variables) => {
// Refetch to ensure consistency
queryClient.invalidateQueries({ queryKey: ['users', variables.id] });
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}
import { useInfiniteQuery } from '@tanstack/react-query';
interface Page {
data: User[];
nextCursor?: string;
}
async function fetchUsersPage({ pageParam }: { pageParam?: string }): Promise<Page> {
const url = pageParam
? `/api/users?cursor=${pageParam}`
: '/api/users';
const response = await fetch(url);
return response.json();
}
export function useInfiniteUsers() {
return useInfiniteQuery({
queryKey: ['users', 'infinite'],
queryFn: fetchUsersPage,
initialPageParam: undefined,
getNextPageParam: (lastPage) => lastPage.nextCursor,
getPreviousPageParam: (firstPage) => firstPage.previousCursor,
});
}
// Usage in component
function UserList() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteUsers();
return (
<div>
{data?.pages.map((page, i) => (
<React.Fragment key={i}>
{page.data.map((user) => (
<UserCard key={user.id} user={user} />
))}
</React.Fragment>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage ? 'Loading...' : hasNextPage ? 'Load More' : 'No more'}
</button>
</div>
);
}
import { useQueryClient } from '@tanstack/react-query';
function UserLink({ userId }: { userId: string }) {
const queryClient = useQueryClient();
const prefetchUser = () => {
queryClient.prefetchQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
staleTime: 1000 * 60 * 5,
});
};
return (
<Link
to={`/users/${userId}`}
onMouseEnter={prefetchUser}
onFocus={prefetchUser}
>
View User
</Link>
);
}
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,
};
// Usage
useQuery({ queryKey: userKeys.detail(userId), ... });
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
development
Model documentation skill for generating model cards following Google's model card framework.
development
MLflow integration skill for experiment tracking, model registry, and artifact management. Enables LLMs to log experiments, compare runs, manage model lifecycle, and retrieve artifacts through the MLflow API.
data-ai
LIME-based local explanation skill for individual predictions across tabular, text, and image data.
devops
Kubeflow Pipelines skill for ML workflow orchestration, component management, and Kubernetes-native ML.