> tanstack-query
You are an expert in TanStack Query (formerly React Query), the data-fetching and server state management library. You help developers build React applications with automatic caching, background refetching, optimistic updates, pagination, infinite scroll, and offline support — replacing manual `useEffect` + `useState` patterns with declarative, type-safe data fetching hooks.
curl "https://skillshub.wtf/TerminalSkills/skills/tanstack-query?format=md"TanStack Query — Async State Management for React
You are an expert in TanStack Query (formerly React Query), the data-fetching and server state management library. You help developers build React applications with automatic caching, background refetching, optimistic updates, pagination, infinite scroll, and offline support — replacing manual useEffect + useState patterns with declarative, type-safe data fetching hooks.
Core Capabilities
Basic Queries
import { useQuery, useMutation, useQueryClient, QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 min before refetch
gcTime: 10 * 60 * 1000, // 10 min cache lifetime
retry: 2,
refetchOnWindowFocus: true,
},
},
});
// Wrap app
function App() {
return (
<QueryClientProvider client={queryClient}>
<Dashboard />
</QueryClientProvider>
);
}
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: () => fetch("/api/users").then(r => r.json()),
});
if (isLoading) return <Skeleton />;
if (error) return <Error message={error.message} />;
return <ul>{data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Mutations with Optimistic Updates
function useCreateTodo() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (newTodo: { title: string }) =>
fetch("/api/todos", { method: "POST", body: JSON.stringify(newTodo) }).then(r => r.json()),
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: ["todos"] });
const previous = queryClient.getQueryData(["todos"]);
queryClient.setQueryData(["todos"], (old: Todo[]) => [
...old, { id: "temp", ...newTodo, completed: false },
]);
return { previous };
},
onError: (_err, _todo, context) => {
queryClient.setQueryData(["todos"], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
});
}
Infinite Scroll
function InfiniteFeed() {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
queryKey: ["feed"],
queryFn: ({ pageParam }) => fetch(`/api/feed?cursor=${pageParam}`).then(r => r.json()),
initialPageParam: "",
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
});
return (
<div>
{data?.pages.map(page => page.items.map(item => <FeedItem key={item.id} item={item} />))}
<button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage}>
{isFetchingNextPage ? "Loading..." : hasNextPage ? "Load more" : "No more"}
</button>
</div>
);
}
Installation
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools # Optional dev tools
Best Practices
- Query keys — Use arrays:
["users", userId, { status }]; TanStack auto-invalidates related queries - staleTime — Set based on data freshness needs; 0 = always refetch, 5min for semi-static data
- Optimistic updates — Update cache immediately on mutation; rollback on error for instant UX
- Prefetching — Use
queryClient.prefetchQueryon hover/focus for perceived instant navigation - Infinite queries — Use
useInfiniteQueryfor paginated lists;getNextPageParamhandles cursor logic - Dependent queries — Use
enabledoption:enabled: !!userIdto chain queries that depend on each other - DevTools — Add
<ReactQueryDevtools />in development; shows all queries, cache state, and timings - Select for transforms — Use
selectoption to transform server data in the query; derived data is memoized
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.