---
name: TanStack Query
description: 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.
---
# 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
```bash
npm install @tanstack/react-query
npm install --save-dev @tanstack/react-query-devtools
```
## Setup
```typescript
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 (
);
}
```
## useQuery
```typescript
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
Loading...
;
if (error) return Error: {error.message}
;
return (
{data.map(cert => (
- {cert.subject}
))}
);
}
```
### Query with Parameters
```typescript
function CertificateDetail({ id }: { id: string }) {
const { data: certificate } = useQuery({
queryKey: ['certificate', id],
queryFn: () => fetch(`/api/certificates/${id}`).then(res => res.json()),
});
return {certificate?.subject}
;
}
```
### Dependent Queries
```typescript
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 {ca?.subject}
;
}
```
## useMutation
```typescript
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 (
);
}
```
## Optimistic Updates
```typescript
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
```typescript
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 (
<>
{data?.certificates.map(cert => (
- {cert.subject}
))}
>
);
}
```
## Infinite Queries
```typescript
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) => (
{page.certificates.map(cert => (
{cert.subject}
))}
))}
>
);
}
```
## Cache Invalidation
```typescript
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
```typescript
// 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
1. **Query Keys**: Use arrays for structured keys `['certificates', { status: 'active' }]`
2. **Stale Time**: Set appropriate stale time for your data
3. **Cache Time**: Keep data in cache longer than stale time
4. **Optimistic Updates**: Improve UX with optimistic updates
5. **Error Handling**: Handle errors gracefully
6. **Invalidation**: Invalidate related queries on mutations
7. **Pagination**: Use `keepPreviousData` for better UX
8. **DevTools**: Use DevTools for debugging
9. **TypeScript**: Define types for query data
10. **Prefetching**: Prefetch data for better performance
## Resources
- Documentation: https://tanstack.com/query
- GitHub: https://github.com/TanStack/query