React Query is a server state management library that caches, updates, and synchronizes data fetched from APIs. It handles caching, background refetching, error handling, and optimistic updates automatically.
How it works step-by-step:
useQuery for GET requests — it caches data and refetches in the background.useMutation for POST/PUT/DELETE — it tracks loading state and invalidates related queries.1import {2 QueryClient,3 QueryClientProvider,4 useQuery,5 useMutation,6 useQueryClient7} from "@tanstack/react-query";89const queryClient = new QueryClient({10 defaultOptions: {11 queries: {12 staleTime: 5 * 60 * 1000, // 5 min before data is stale13 gcTime: 30 * 60 * 1000, // 30 min before cache is garbage collected14 retry: 3, // Retry failed requests 3 times15 refetchOnWindowFocus: true // Refetch when window regains focus16 }17 }18});1920// Provider21function App() {22 return (23 <QueryClientProvider client={queryClient}>24 <UserList />25 </QueryClientProvider>26 );27}2829// Query (GET)30function UserList() {31 const { data, isLoading, error, refetch } = useQuery({32 queryKey: ["users"],33 queryFn: async () => {34 const res = await fetch("/api/users");35 if (!res.ok) throw new Error("Failed to load users");36 return res.json();37 }38 });3940 if (isLoading) return <Spinner />;41 if (error) return <div>Error: {error.message}</div>;4243 return (44 <div>45 <button onClick={() => refetch()}>Refresh</button>46 {data.map((user: { id: number; name: string }) => (47 <div key={user.id}>{user.name}</div>48 ))}49 </div>50 );51}5253// Mutation (POST/PUT/DELETE)54function CreateUser() {55 const queryClient = useQueryClient();56 const mutation = useMutation({57 mutationFn: (newUser: { name: string }) =>58 fetch("/api/users", {59 method: "POST",60 headers: { "Content-Type": "application/json" },61 body: JSON.stringify(newUser)62 }).then(r => r.json()),63 onSuccess: () => {64 // Invalidate the users query to refetch65 queryClient.invalidateQueries({ queryKey: ["users"] });66 }67 });6869 return (70 <button71 onClick={() => mutation.mutate({ name: "New User" })}72 disabled={mutation.isPending}73 >74 {mutation.isPending ? "Creating..." : "Create user"}75 </button>76 );77}7879// Optimistic update example80function TodoList() {81 const queryClient = useQueryClient();82 const { data: todos } = useQuery({83 queryKey: ["todos"],84 queryFn: () => fetch("/api/todos").then(r => r.json())85 });8687 const toggleMutation = useMutation({88 mutationFn: (todo: { id: number; done: boolean }) =>89 fetch("/api/todos/" + todo.id, {90 method: "PATCH",91 headers: { "Content-Type": "application/json" },92 body: JSON.stringify({ done: todo.done })93 }).then(r => r.json()),94 onMutate: async (newTodo) => {95 await queryClient.cancelQueries({ queryKey: ["todos"] });96 const previous = queryClient.getQueryData(["todos"]);97 queryClient.setQueryData(["todos"], (old: { id: number; done: boolean }[]) =>98 old.map(t => t.id === newTodo.id ? { ...t, done: newTodo.done } : t)99 );100 return { previous };101 },102 onError: (err, newTodo, context) => {103 queryClient.setQueryData(["todos"], context?.previous);104 },105 onSettled: () => {106 queryClient.invalidateQueries({ queryKey: ["todos"] });107 }108 });109110 return (111 <ul>112 {todos?.map((todo: { id: number; text: string; done: boolean }) => (113 <li key={todo.id}>114 <input115 type="checkbox"116 checked={todo.done}117 onChange={() => toggleMutation.mutate({ id: todo.id, done: !todo.done })}118 />119 {todo.text}120 </li>121 ))}122 </ul>123 );124}
Benefits: Automatic caching, background updates, optimistic updates, pagination, infinite scroll, deduplication of requests.
Configuration options:
staleTime — how long data is considered fresh (no refetch).gcTime — how long unused data stays in cache.retry — number of retry attempts for failed queries.refetchOnWindowFocus — refetch when the tab regains focus.refetchOnMount — refetch when the component mounts.Performance considerations:
select to transform data and prevent unnecessary re-renders.enabled to conditionally run queries.keepPreviousData for pagination to avoid flicker.