Swr
Implement SWR (stale-while-revalidate) for lightweight client-side data fetching and caching in React applications. Configure revalidation strategies, mutations, and global settings for responsive UIs with minimal boilerplate.
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: 206 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
Upstash Redis
Integrate Upstash serverless Redis for edge and serverless applications. Use the HTTP-based REST API for Vercel Edge Functions, Cloudflare Workers, and other environments without persistent TCP connections.
Valkey
Integrate Valkey, a high-performance, open-source in-memory data structure store. Leverage its versatile data structures and Redis-compatible API for lightning-fast caching, real-time analytics, session management, and message brokering in demanding web applications.
Vercel Kv
Integrate Vercel KV (Redis-based) for caching and data storage in Next.js applications. Use the built-in SDK for edge-compatible key-value operations, rate limiting, and session management on Vercel's platform.
Apache Ignite
Integrate Apache Ignite, a high-performance, fault-tolerant distributed in-memory data grid. Leverage its capabilities for caching, database, and processing to achieve extreme scalability and low-latency data access in demanding web applications, making it ideal for real-time analytics, transactional systems, and compute-intensive tasks.
Cloudflare Kv
Integrate Cloudflare Workers KV for globally distributed edge key-value storage. Implement eventually consistent caching, static asset serving, and configuration management in Cloudflare Workers and Pages Functions.
Dragonfly
Integrate Dragonfly, a high-performance, in-memory data store compatible with Redis and Memcached APIs. Leverage its efficiency for caching, real-time data, and session management, reducing resource consumption and improving throughput for demanding web applications.