Skills tanstack-query
install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tanstack-query" ~/.claude/skills/terminalskills-skills-tanstack-query && rm -rf "$T"
manifest:
skills/tanstack-query/SKILL.mdsource content
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:
; TanStack auto-invalidates related queries["users", userId, { status }] - 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
on hover/focus for perceived instant navigationqueryClient.prefetchQuery - Infinite queries — Use
for paginated lists;useInfiniteQuery
handles cursor logicgetNextPageParam - Dependent queries — Use
option:enabled
to chain queries that depend on each otherenabled: !!userId - DevTools — Add
in development; shows all queries, cache state, and timings<ReactQueryDevtools /> - Select for transforms — Use
option to transform server data in the query; derived data is memoizedselect