Why You'll Reach for React Query or SWR
Data Fetching Libraries
Hand-rolled `useEffect` + `fetch` covers basics. For real apps, React Query and SWR give you caching, deduping, retries, and revalidation — for free.
What you'll learn
- Recognize the pain points of raw `useEffect` fetching
- Know what React Query / SWR provide
- Pick when each is the right tool
You’ve seen how to fetch with useEffect + fetch. For an actual
app, that approach grows pain quickly:
- Two components fetching the same data make two requests
- Tabbing away and back doesn’t refresh stale data
- Refetching after a mutation requires manual plumbing
- Loading and error states get repeated everywhere
- Optimistic updates are tricky
Libraries solve all of this with a small API.
React Query (TanStack Query)
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
function UserCard({ id }) {
const { data, error, isLoading } = useQuery({
queryKey: ["user", id],
queryFn: () => fetch(`/api/users/${id}`).then(r => r.json()),
});
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return <p>{data.name}</p>;
} You get:
- Caching — the second
UserCardwith the same id reads from cache - Background refetching — when the user tabs back, refresh stale data
- Deduping — multiple components asking for the same key make one request
- Retries — failed requests retry on a backoff
- Optimistic updates — built-in helpers
Mutations
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: newUser => fetch("/api/users", { method: "POST", body: JSON.stringify(newUser) }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
<button onClick={() => mutation.mutate(form)}>Save</button> After the save succeeds, every query that depends on ["users"]
refetches automatically.
SWR
Similar idea, smaller API:
import useSWR from "swr";
function UserCard({ id }) {
const { data, error, isLoading } = useSWR(`/api/users/${id}`, url => fetch(url).then(r => r.json()));
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error</p>;
return <p>{data.name}</p>;
} Pick One
| Library | Strengths |
|---|---|
| React Query | Comprehensive — caching, mutations, devtools, infinite queries, suspense |
| SWR | Smaller, simpler — great fit for read-heavy apps |
Pick by what your team prefers. Both are excellent. For new apps, React Query is the more common default.
When You Don’t Need Them
- Static data that doesn’t change
- Forms that just submit once
- Toy apps and demos
For everything else with non-trivial server state — use a library.
Up Next
What about state that isn’t from the server?
State Management →