React Query / SWR
Fetching data in React apps can get complex — especially when you need caching, background updates, refetching, or mutations. Libraries like React Query and SWR simplify data fetching, making your components faster, more responsive, and easier to maintain.
Both libraries handle remote data management, caching, and state updates, allowing you to focus on building UI instead of managing network requests manually.
Why Use React Query or SWR?
- Automatic caching – Previously fetched data is stored and instantly available, reducing unnecessary network requests.
- Background updates – Data can refresh automatically in the background to keep the UI up-to-date.
- Simplified error handling – Built-in support for retries, error states, and fallbacks.
- Optimistic updates & mutations – Seamlessly update UI while sending data to the server.
- Server state management – Keeps remote data synchronized with your React components.
React Query – Basics
Installation:
npm install @tanstack/react-query
Setup: Wrap your app with QueryClientProvider:
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import UsersList from "./UsersList";
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<UsersList />
</QueryClientProvider>
);
}
export default App;
Fetching Data with useQuery:
import React from "react";
import { useQuery } from "@tanstack/react-query";
async function fetchUsers() {
const response = await fetch("<https://jsonplaceholder.typicode.com/users>");
if (!response.ok) throw new Error("Network Error");
return response.json();
}
function UsersList() {
const { data, isLoading, error } = useQuery(["users"], fetchUsers);
if (isLoading) return <p>Loading users...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UsersList;
How it works:
useQueryfetches data and caches it automatically.isLoadinganderrorhelp manage UI states.- The
["users"]key uniquely identifies the cached query.
Mutations – Updating Data
React Query also handles mutations to update or post data:
import { useMutation, useQueryClient } from "@tanstack/react-query";
function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation(
(newUser) => fetch("<https://jsonplaceholder.typicode.com/users>", {
method: "POST",
body: JSON.stringify(newUser),
headers: { "Content-Type": "application/json" }
}),
{
onSuccess: () => queryClient.invalidateQueries(["users"])
}
);
return (
<buttononClick={() => mutation.mutate({ name: "New User" })}
>
Add User
</button>
);
}
invalidateQueriesensures cached data updates automatically after a mutation.
SWR – Simple and Lightweight Alternative
SWR is another popular library by Vercel, focusing on stale-while-revalidate caching:
- Automatic caching and revalidation.
- Optimistic UI updates.
- Minimal configuration.
- Works seamlessly with React functional components.
Installation:
npm install swr
Usage:
import useSWR from "swr";
const fetcher = (url) => fetch(url).then(res => res.json());
function UsersList() {
const { data, error } = useSWR("<https://jsonplaceholder.typicode.com/users>", fetcher);
if (!data) return <p>Loading users...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UsersList;
Tips for Efficient API Integration
- Use React Query or SWR for repeated API calls — avoids unnecessary network requests.
- Handle loading, error, and empty states for better UX.
- Use query keys wisely to manage caching properly.
- Mutations for create/update/delete operations keep the UI and server data in sync.
- Combine pagination or infinite scroll with caching to improve performance on large datasets.
