---
name: "add-tanstack-query"
description: "Add TanStack Query (React Query) for server state in an Expo app. Use when the user says 'TanStack Query', 'React Query', 'server state', 'data fetching', 'API calls'."
---
# Add TanStack Query
Server state, caching, refetching, mutations, optimistic UI.
## Install
```bash
npm install @tanstack/react-query
```
## Setup in root
```tsx
// app/_layout.tsx
import { QueryClient, QueryClientProvider, focusManager } from '@tanstack/react-query';
import { AppState } from 'react-native';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60, // 1 min
gcTime: 1000 * 60 * 5, // 5 min
retry: 2,
},
},
});
// refresh on app foreground
AppState.addEventListener('change', (state) => {
focusManager.setFocused(state === 'active');
});
export default function RootLayout() {
return {/* ... */};
}
```
## Query
```tsx
import { useQuery } from '@tanstack/react-query';
function Feed() {
const { data, isLoading, error } = useQuery({
queryKey: ['posts'],
queryFn: async () => fetch('/api/posts').then(r => r.json()),
});
if (isLoading) return ;
if (error) return ;
if (!data?.length) return ;
return } />;
}
```
## Mutation with optimistic UI
```tsx
const queryClient = useQueryClient();
const like = useMutation({
mutationFn: (postId: string) => fetch(`/api/posts/${postId}/like`, { method: 'POST' }),
onMutate: async (postId) => {
await queryClient.cancelQueries({ queryKey: ['posts'] });
const prev = queryClient.getQueryData(['posts']);
queryClient.setQueryData(['posts'], (old: Post[]) =>
old.map(p => p.id === postId ? { ...p, liked: true } : p)
);
return { prev };
},
onError: (err, postId, ctx) => queryClient.setQueryData(['posts'], ctx.prev),
});
```
## Reference
`references/01-expo-sdk-54.md`