SWR (stale-while-revalidate) is Vercel's data-fetching library for React. Its API is deceptively simple — but under the hood, it handles caching, revalidation, pagination, and optimistic updates.
Basic Usage: One Hook, Full Cache
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then(r => r.json());
function Profile() {
const { data, error, isLoading, isValidating, mutate } = useSWR(
"/api/user",
fetcher
);
if (isLoading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <div>{data.name} {isValidating && <RefreshIcon />}</div>;
}
SWR returns stale data instantly, then revalidates in the background.
Conditional Fetching
// Only fetch when userId exists
const { data } = useSWR(userId ? `/api/users/${userId}` : null, fetcher);
// Dependent fetching — wait for first request
const { data: user } = useSWR("/api/user", fetcher);
const { data: projects } = useSWR(
user ? `/api/projects?userId=${user.id}` : null,
fetcher
);
Global Configuration
import { SWRConfig } from "swr";
function App() {
return (
<SWRConfig value={{
fetcher: (url) => fetch(url).then(r => r.json()),
refreshInterval: 30000,
revalidateOnFocus: true,
revalidateOnReconnect: true,
dedupingInterval: 2000,
errorRetryCount: 3,
onError: (error) => Sentry.captureException(error),
}}>
<Dashboard />
</SWRConfig>
);
}
Mutation: Optimistic Updates
const { data, mutate } = useSWR("/api/todos", fetcher);
async function addTodo(newTodo) {
// Optimistically update UI
await mutate(
async (currentData) => {
const res = await fetch("/api/todos", {
method: "POST",
body: JSON.stringify(newTodo)
});
const created = await res.json();
return [...currentData, created];
},
{
optimisticData: [...data, { ...newTodo, id: "temp" }],
rollbackOnError: true,
revalidate: false,
}
);
}
Infinite Loading: useSWRInfinite
import useSWRInfinite from "swr/infinite";
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.length) return null; // End
return `/api/products?page=${pageIndex}&limit=20`;
};
function ProductList() {
const { data, size, setSize, isValidating } = useSWRInfinite(getKey, fetcher);
const products = data ? data.flat() : [];
const isEnd = data && data[data.length - 1]?.length < 20;
return (
<>
{products.map(p => <ProductCard key={p.id} product={p} />)}
{!isEnd && (
<button onClick={() => setSize(size + 1)} disabled={isValidating}>
Load More
</button>
)}
</>
);
}
Subscription: Real-Time Data
import useSWRSubscription from "swr/subscription";
function LivePrices({ symbol }) {
const { data } = useSWRSubscription(`prices/${symbol}`, (key, { next }) => {
const ws = new WebSocket(`wss://stream.example.com/${key}`);
ws.onmessage = (e) => next(null, JSON.parse(e.data));
ws.onerror = (e) => next(e);
return () => ws.close();
});
return <span>${data?.price}</span>;
}
Fetch scraped data with SWR? My Apify tools provide API endpoints perfect for SWR integration.
Custom data fetching solution? Email spinov001@gmail.com
Top comments (0)