SWR data-fetching expert guidance. Use when building React apps with client-side data fetching, caching, revalidation, mutations, optimistic UI, pagination, or infinite loading using the SWR library.
68
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
You are an expert in SWR v2 (latest: 2.4.1), the React Hooks library for data fetching by Vercel. SWR implements the stale-while-revalidate HTTP cache invalidation strategy — serve from cache first, then revalidate in the background.
npm install swruseSWRimport useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then(res => res.json())
function Profile() {
const { data, error, isLoading, mutate } = useSWR('/api/user', fetcher)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error loading data</div>
return <div>Hello, {data.name}</div>
}Key parameters:
key — unique string, array, or function identifying the resource (often a URL)fetcher — async function that receives the key and returns dataoptions — optional config objectReturn values: data, error, isLoading, isValidating, mutate
useSWRMutation — Remote Mutationsimport useSWRMutation from 'swr/mutation'
async function updateUser(url: string, { arg }: { arg: { name: string } }) {
return fetch(url, { method: 'POST', body: JSON.stringify(arg) }).then(res => res.json())
}
function Profile() {
const { trigger, isMutating } = useSWRMutation('/api/user', updateUser)
return (
<button disabled={isMutating} onClick={() => trigger({ name: 'New Name' })}>
Update
</button>
)
}useSWRInfinite — Pagination & Infinite Loadingimport useSWRInfinite from 'swr/infinite'
const getKey = (pageIndex: number, previousPageData: any[]) => {
if (previousPageData && !previousPageData.length) return null
return `/api/items?page=${pageIndex}`
}
function Items() {
const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher)
const items = data ? data.flat() : []
return (
<>
{items.map(item => <div key={item.id}>{item.name}</div>)}
<button onClick={() => setSize(size + 1)}>Load More</button>
</>
)
}Wrap your app (or a subtree) with SWRConfig to set defaults:
import { SWRConfig } from 'swr'
function App() {
return (
<SWRConfig value={{
fetcher: (url: string) => fetch(url).then(res => res.json()),
revalidateOnFocus: false,
dedupingInterval: 5000,
}}>
<Dashboard />
</SWRConfig>
)
}| Strategy | Option | Default |
|---|---|---|
| On window focus | revalidateOnFocus | true |
| On network recovery | revalidateOnReconnect | true |
| On mount if stale | revalidateIfStale | true |
| Polling | refreshInterval | 0 (disabled) |
| Manual | Call mutate() | — |
const { trigger } = useSWRMutation('/api/user', updateUser, {
optimisticData: (current) => ({ ...current, name: 'New Name' }),
rollbackOnError: true,
populateCache: true,
revalidate: false,
})Pass null or a falsy key to skip fetching:
const { data } = useSWR(userId ? `/api/user/${userId}` : null, fetcher)SWR retries on error by default with exponential backoff. Customize with:
useSWR(key, fetcher, {
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
if (error.status === 404) return // Don't retry on 404
if (retryCount >= 3) return // Max 3 retries
setTimeout(() => revalidate({ retryCount }), 5000)
},
})useSWRSubscription — Real-Time Data SourcesSubscribe to real-time data (WebSockets, SSE, etc.) with automatic deduplication:
import useSWRSubscription from 'swr/subscription'
function LivePrice({ symbol }: { symbol: string }) {
const { data } = useSWRSubscription(
`wss://stream.example.com/${symbol}`,
(key, { next }) => {
const ws = new WebSocket(key)
ws.onmessage = (event) => next(null, JSON.parse(event.data))
ws.onerror = (event) => next(event)
return () => ws.close()
}
)
return <span>{data?.price}</span>
}The subscribe function receives a next(error, data) callback and must return a cleanup function. Multiple components using the same key share a single subscription.
useSWR calls with the same key share cache and deduplicate requestsSWRConfigmutate(key) globally revalidates any hook matching that keyuseSWR(['/api/user', id], fetcher) — the fetcher receives the full arraynull) insteadd416fd5
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.