Agent skill
TanStack Query
Expert guidance for TanStack Query (React Query) including queries, mutations, caching, invalidation, optimistic updates, pagination, and infinite queries. Use this when managing server state in React applications.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/design/tanstack-query-oriolrius-pki-manager-web
SKILL.md
TanStack Query (React Query)
Expert assistance with TanStack Query - Powerful data fetching for React.
Overview
TanStack Query manages server state in React:
- Caching: Automatic caching and background updates
- Refetching: Smart refetch strategies
- Mutations: Optimistic updates and cache invalidation
- DevTools: Built-in development tools
- TypeScript: Full TypeScript support
Installation
npm install @tanstack/react-query
npm install --save-dev @tanstack/react-query-devtools
Setup
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
cacheTime: 5 * 60 * 1000, // 5 minutes
refetchOnWindowFocus: false,
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
useQuery
import { useQuery } from '@tanstack/react-query';
function CertificateList() {
const { data, isLoading, error } = useQuery({
queryKey: ['certificates'],
queryFn: () => fetch('/api/certificates').then(res => res.json()),
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(cert => (
<li key={cert.id}>{cert.subject}</li>
))}
</ul>
);
}
Query with Parameters
function CertificateDetail({ id }: { id: string }) {
const { data: certificate } = useQuery({
queryKey: ['certificate', id],
queryFn: () => fetch(`/api/certificates/${id}`).then(res => res.json()),
});
return <div>{certificate?.subject}</div>;
}
Dependent Queries
function Certificate({ id }: { id: string }) {
const { data: certificate } = useQuery({
queryKey: ['certificate', id],
queryFn: () => fetchCertificate(id),
});
// Only run if certificate exists
const { data: ca } = useQuery({
queryKey: ['ca', certificate?.caId],
queryFn: () => fetchCA(certificate.caId),
enabled: !!certificate?.caId,
});
return <div>{ca?.subject}</div>;
}
useMutation
import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateCertificate() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newCert) => {
return fetch('/api/certificates', {
method: 'POST',
body: JSON.stringify(newCert),
});
},
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ['certificates'] });
},
});
return (
<button
onClick={() => mutation.mutate({ subject: 'CN=example.com' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Creating...' : 'Create Certificate'}
</button>
);
}
Optimistic Updates
const mutation = useMutation({
mutationFn: updateCertificate,
onMutate: async (newCert) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['certificates'] });
// Snapshot previous value
const previousCerts = queryClient.getQueryData(['certificates']);
// Optimistically update
queryClient.setQueryData(['certificates'], (old) =>
old.map((cert) =>
cert.id === newCert.id ? newCert : cert
)
);
return { previousCerts };
},
onError: (err, newCert, context) => {
// Rollback on error
queryClient.setQueryData(['certificates'], context.previousCerts);
},
onSettled: () => {
// Refetch after success or error
queryClient.invalidateQueries({ queryKey: ['certificates'] });
},
});
Pagination
function CertificateList() {
const [page, setPage] = useState(1);
const { data, isLoading } = useQuery({
queryKey: ['certificates', page],
queryFn: () => fetchCertificates(page),
keepPreviousData: true, // Keep old data while fetching new
});
return (
<>
<ul>
{data?.certificates.map(cert => (
<li key={cert.id}>{cert.subject}</li>
))}
</ul>
<button onClick={() => setPage(p => p - 1)} disabled={page === 1}>
Previous
</button>
<button onClick={() => setPage(p => p + 1)} disabled={!data?.hasMore}>
Next
</button>
</>
);
}
Infinite Queries
import { useInfiniteQuery } from '@tanstack/react-query';
function InfiniteCertificates() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['certificates'],
queryFn: ({ pageParam = 1 }) => fetchCertificates(pageParam),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
initialPageParam: 1,
});
return (
<>
{data?.pages.map((page, i) => (
<div key={i}>
{page.certificates.map(cert => (
<div key={cert.id}>{cert.subject}</div>
))}
</div>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage ? 'Loading...' : 'Load More'}
</button>
</>
);
}
Cache Invalidation
const queryClient = useQueryClient();
// Invalidate all queries
queryClient.invalidateQueries();
// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['certificates'] });
// Invalidate query with params
queryClient.invalidateQueries({ queryKey: ['certificate', '123'] });
// Invalidate all queries starting with key
queryClient.invalidateQueries({ queryKey: ['certificates'], exact: false });
// Remove query from cache
queryClient.removeQueries({ queryKey: ['certificates'] });
// Reset query to initial state
queryClient.resetQueries({ queryKey: ['certificates'] });
Manual Cache Updates
// Set query data
queryClient.setQueryData(['certificate', '123'], newCertificate);
// Update query data
queryClient.setQueryData(['certificates'], (old) =>
old.map((cert) => cert.id === '123' ? updated : cert)
);
// Get query data
const certificates = queryClient.getQueryData(['certificates']);
// Prefetch query
await queryClient.prefetchQuery({
queryKey: ['certificate', '123'],
queryFn: () => fetchCertificate('123'),
});
Best Practices
- Query Keys: Use arrays for structured keys
['certificates', { status: 'active' }] - Stale Time: Set appropriate stale time for your data
- Cache Time: Keep data in cache longer than stale time
- Optimistic Updates: Improve UX with optimistic updates
- Error Handling: Handle errors gracefully
- Invalidation: Invalidate related queries on mutations
- Pagination: Use
keepPreviousDatafor better UX - DevTools: Use DevTools for debugging
- TypeScript: Define types for query data
- Prefetching: Prefetch data for better performance
Resources
- Documentation: https://tanstack.com/query
- GitHub: https://github.com/TanStack/query
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?