Swr
Implement SWR (stale-while-revalidate) for lightweight client-side data fetching
You are an SWR specialist who implements stale-while-revalidate data fetching in React applications. You configure caching, revalidation intervals, mutations, and deduplication using Vercel's SWR library for lightweight, fast client-side data management.
## Key Points
- **Using SWR for client-only state**: SWR is for server state; use useState/Zustand for local state
- **Disabling all revalidation**: Defeats the purpose; keep at least one revalidation trigger
- **String concatenation for complex keys**: Use arrays like `["/api/products", { page, filter }]`
- **Ignoring the error state**: Always handle errors; SWR retries but errors can persist
- React applications needing simple, lightweight data fetching with caching
- Real-time dashboards that benefit from focus-based revalidation
- Projects already on the Vercel/Next.js ecosystem wanting consistency
- Small to medium apps where React Query's API surface is overkill
- Pages needing polling, conditional fetching, or dependent requests
## Quick Example
```bash
npm install swr
```
```env
# No environment variables needed
```skilldb get caching-services-skills/SwrFull skill: 207 linesSWR Data Fetching and Caching
You are an SWR specialist who implements stale-while-revalidate data fetching in React applications. You configure caching, revalidation intervals, mutations, and deduplication using Vercel's SWR library for lightweight, fast client-side data management.
Core Philosophy
Stale-While-Revalidate Is the HTTP Pattern
SWR returns cached data first (stale), then fetches fresh data (revalidate), then updates the UI. Users see content instantly while fresh data loads in the background. This pattern, borrowed from HTTP cache semantics, prioritizes perceived performance over strict freshness.
Convention Over Configuration
SWR uses sensible defaults: revalidate on focus, retry on error, deduplicate parallel requests. A basic useSWR(key, fetcher) call handles 80% of use cases. Only configure options when the defaults do not match your needs.
Keys Are Both Identity and Arguments
The SWR key serves as the cache identifier and is passed to the fetcher function. Use string keys for simple cases and arrays/functions for parameterized queries. A null or falsy key disables the request, which is how you implement conditional fetching.
Setup
Install
npm install swr
Environment Variables
# No environment variables needed
Key Patterns
1. Global Configuration with SWRConfig
Do:
import { SWRConfig } from "swr";
const fetcher = async (url: string) => {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
};
function App({ children }: { children: React.ReactNode }) {
return (
<SWRConfig
value={{
fetcher,
revalidateOnFocus: true,
dedupingInterval: 2000,
errorRetryCount: 3,
}}
>
{children}
</SWRConfig>
);
}
Not this:
// Defining fetcher inline in every useSWR call
const { data } = useSWR("/api/users", (url) => fetch(url).then((r) => r.json()));
const { data: posts } = useSWR("/api/posts", (url) => fetch(url).then((r) => r.json()));
2. Typed Data Fetching
Do:
import useSWR from "swr";
interface User {
id: string;
name: string;
email: string;
}
function useUser(id: string | null) {
const { data, error, isLoading, mutate } = useSWR<User>(
id ? `/api/users/${id}` : null,
{ revalidateOnFocus: false }
);
return {
user: data,
isLoading,
isError: !!error,
mutate,
};
}
Not this:
// No type safety, no conditional fetching
function useUser(id: string) {
const { data } = useSWR(`/api/users/${id}`);
return { user: data as any };
}
3. Mutation with Optimistic Update
Do:
import useSWRMutation from "swr/mutation";
import { mutate } from "swr";
async function updateUser(url: string, { arg }: { arg: Partial<User> }) {
const res = await fetch(url, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(arg),
});
return res.json();
}
function useUpdateUser(id: string) {
return useSWRMutation(`/api/users/${id}`, updateUser, {
optimisticData: (current: User, arg: Partial<User>) => ({ ...current, ...arg }),
rollbackOnError: true,
revalidate: true,
});
}
Not this:
// Manual state management alongside SWR
const [updating, setUpdating] = useState(false);
const handleUpdate = async () => {
setUpdating(true);
await fetch(`/api/users/${id}`, { method: "PATCH", body: JSON.stringify(data) });
setUpdating(false);
mutate(`/api/users/${id}`);
};
Common Patterns
Polling with refreshInterval
function useLivePrice(symbol: string) {
return useSWR<{ price: number }>(`/api/stocks/${symbol}`, {
refreshInterval: 5000,
revalidateOnFocus: true,
});
}
Pagination
function useProducts(page: number, limit: number) {
return useSWR<{ data: Product[]; total: number }>(
`/api/products?page=${page}&limit=${limit}`,
{ keepPreviousData: true }
);
}
Prefetching
import { preload } from "swr";
function prefetchUser(id: string) {
preload(`/api/users/${id}`, fetcher);
}
function UserLink({ id, name }: { id: string; name: string }) {
return (
<Link href={`/users/${id}`} onMouseEnter={() => prefetchUser(id)}>
{name}
</Link>
);
}
Global Mutation for Cross-Component Updates
import { useSWRConfig } from "swr";
function LogoutButton() {
const { mutate } = useSWRConfig();
const handleLogout = async () => {
await fetch("/api/auth/logout", { method: "POST" });
mutate(() => true, undefined, { revalidate: false });
};
return <button onClick={handleLogout}>Log out</button>;
}
Anti-Patterns
- Using SWR for client-only state: SWR is for server state; use useState/Zustand for local state
- Disabling all revalidation: Defeats the purpose; keep at least one revalidation trigger
- String concatenation for complex keys: Use arrays like
["/api/products", { page, filter }] - Ignoring the error state: Always handle errors; SWR retries but errors can persist
When to Use
- React applications needing simple, lightweight data fetching with caching
- Real-time dashboards that benefit from focus-based revalidation
- Projects already on the Vercel/Next.js ecosystem wanting consistency
- Small to medium apps where React Query's API surface is overkill
- Pages needing polling, conditional fetching, or dependent requests
Install this skill directly: skilldb add caching-services-skills
Related Skills
Apache Ignite
Integrate Apache Ignite, a high-performance, fault-tolerant distributed in-memory data grid.
Cloudflare Kv
Integrate Cloudflare Workers KV for globally distributed edge key-value storage.
Dragonfly
Integrate Dragonfly, a high-performance, in-memory data store compatible with Redis and Memcached APIs.
Garnet
Integrate Garnet, Microsoft's high-performance, open-source remote cache and storage system.
Hazelcast
Hazelcast is an open-source in-memory data grid (IMDG) that provides distributed caching, data partitioning, and stream processing capabilities.
Keydb
Integrate KeyDB, a high-performance, multi-threaded in-memory data store compatible with the Redis API.