React Query
Server state management — caching, revalidation, retries and background updates for data you fetch.
What it is
React Query is a powerful data-fetching library for React that simplifies fetching, caching, synchronizing, and updating server state in your React applications.
React Query provides hooks like `useQuery`, `useMutation`, and `useInfiniteQuery` to fetch and mutate data. It handles caching, background refetching, pagination, and stale data management automatically.
- Best known for
- Making the distinction between server state and client state obvious
- Licence
- MIT
When to use it
The question documentation cannot answer for you — because it cannot recommend something else.
Reach for it when
- Any application that reads data from an API and needs it cached and kept fresh
- You are currently writing loading and error state by hand in every component
- You want optimistic updates, pagination or infinite scroll without building the machinery
Look elsewhere when
- The app fetches once at startup and never again
- You are on a framework with its own data layer that already handles caching
Installation
npm install @tanstack/react-queryGetting started
The smallest useful thing you can do with it, and what each part means.
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
function App() {
const { data, error, isLoading } = useQuery(['todos'], () => axios.get('/api/todos').then(res => res.data));
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.title}</li>)}
</ul>
);
}Advanced usage
Where the library earns its place over a simpler alternative.
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
function AddTodo() {
const queryClient = useQueryClient();
const mutation = useMutation(newTodo => axios.post('/api/todos', newTodo), {
onSuccess: () => queryClient.invalidateQueries(['todos'])
});
return <button onClick={() => mutation.mutate({ title: 'New Todo' })}>Add Todo</button>;
}const { data, isFetching, fetchNextPage } = useInfiniteQuery(['todos'], fetchTodos, {
getNextPageParam: lastPage => lastPage.nextCursor
});const queryClient = useQueryClient();
queryClient.invalidateQueries(['todos']);queryClient.prefetchQuery(['todos'], fetchTodos);Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Network error or failed request
- Handle errors using the `error` property returned by `useQuery` or `useMutation` and provide fallback UI.
- Stale data shown
- Adjust `staleTime` to control when data is considered fresh and triggers background refetch.
- Query not updating after mutation
- Ensure to invalidate relevant queries using `queryClient.invalidateQueries` after successful mutations.
Best practices
- Use `useQuery` for fetching data and `useMutation` for modifying data.
- Leverage query keys for caching and invalidation granularity.
- Use `staleTime` and `cacheTime` wisely to balance performance and freshness.
- Prefetch data when possible for faster UI transitions.
- Combine with `useQueryClient` for advanced cache manipulation and query invalidation.
Alternatives
Comparable options, and the reason you would pick one over the other.
Background
Why it exists, and what it was reacting to.
React Query was created by Tanner Linsley to solve common challenges in managing server state in React apps, such as caching, background updates, and synchronization. It allows developers to focus on building UI while React Query manages the complexity of data fetching and caching efficiently.
