Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
SWR uses an in-memory cache by default. Customize the cache provider for persistent storage, design your key strategy for maximum cache hits, and leverage deduplication to avoid redundant network requests.
"use client";
import { SWRConfig } from "swr";
function App({ children }: { children: React.ReactNode }) {
return (
<SWRConfig
value={{
provider: () => new Map(), // Default in-memory cache
dedupingInterval: 2000, // Dedup requests within 2s
}}
>
{children}
</SWRConfig>
);
}"use client";
import { SWRConfig, Cache } from "swr";
import useSWR from "swr";
// Persistent cache backed by localStorage
function localStorageProvider(): Cache {
const map = new Map<string, any>(
JSON.parse(localStorage.getItem("swr-cache") || "[]")
);
// Persist to localStorage before page unload
window.addEventListener("beforeunload", () => {
const appCache = JSON.stringify(Array.from(map.entries()));
localStorage.setItem("swr-cache", appCache);
});
return {
get: (key: string) => map.get(key),
set: (key: string, value: any) => map.set(key, value),
delete: (key: string) => map.delete(key),
keys: () => map.keys(),
};
}
export function CachedApp({ children }: { children: React.ReactNode }) {
return (
<SWRConfig value={{ provider: localStorageProvider }}>
{children}
</SWRConfig>
);
}
// Components automatically benefit from persistent cache
function UserProfile() {
const { data } = useSWR("/api/me", (url) => fetch(url).then((r) => r.json()));
return <div>{data?.name}</div>;
}useSWR(key) maps directly to a cache entry.Map instance that lives in memory and resets on page reload.dedupingInterval ms, only one fetch runs. All hooks receive the same promise.provider function in SWRConfig is called once on mount and must return a Cache-compatible object.Key design for cache efficiency:
// Good: Stable, serializable keys
useSWR(`/api/users/${id}`, fetcher);
useSWR(["/api/users", id, "posts"], fetcher);
// Bad: Unstable object references (not serializable)
useSWR({ url: "/api/users", id }, fetcher); // Will not cache properlyPrefetching into cache:
import { preload } from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
// Prefetch on hover or route prefetch
function ProductCard({ id }: { id: string }) {
const handleMouseEnter = () => {
preload(`/api/products/${id}`, fetcher);
};
return (
<a href={`/products/${id}`} onMouseEnter={handleMouseEnter}>
View Product
</a>
);
}Clearing the cache:
import { useSWRConfig } from "swr";
function LogoutButton() {
const { cache } = useSWRConfig();
const handleLogout = () => {
// Clear all cached data on logout
if (cache instanceof Map) {
cache.clear();
}
};
return <button onClick={handleLogout}>Logout</button>;
}Cache with TTL:
function ttlProvider(): Cache {
const cache = new Map<string, { value: any; expiry: number }>();
const TTL = 5 * 60 * 1000; // 5 minutes
return {
get: (key: string) => {
const entry = cache.get(key);
if (!entry) return undefined;
if (Date.now() > entry.expiry) {
cache.delete(key);
return undefined;
}
return entry.value;
},
set: (key: string, value: any) => {
cache.set(key, { value, expiry: Date.now() + TTL });
},
delete: (key: string) => cache.delete(key),
keys: () => cache.keys(),
};
}Cache from swr for typing custom providers.provider option expects () => Cache.import type { Cache } from "swr";
const provider = (): Cache => {
const map = new Map();
return {
get: (key: string) => map.get(key),
set: (key: string, value: any) => map.set(key, value),
delete: (key: string) => map.delete(key),
keys: () => map.keys(),
};
};dedupingInterval only deduplicates concurrent requests. It does not prevent re-fetching after the interval expires.["/api/users", id] are serialized for caching. The order of elements matters: [a, b] and [b, a] are different cache keys.Cache interface including keys(). Missing methods will cause runtime errors.set method.SWRConfig providers at different nesting levels; each provider creates its own scope.| Approach | Pros | Cons |
|---|---|---|
| Default Map cache | Zero config, fast | Lost on reload |
| localStorage provider | Persists across sessions | 5MB limit, sync API blocks main thread |
| IndexedDB provider | Large storage, async | More complex implementation |
| No cache (dedupingInterval: 0) | Always fresh data | More network requests |
SWR uses an in-memory Map instance by default. It resets on page reload. No configuration is needed for basic caching.
Implement a custom cache provider backed by localStorage or IndexedDB. The provider must implement the Cache interface (get, set, delete, keys). Persist on beforeunload and rehydrate on initialization.
dedupingInterval) prevents concurrent duplicate requests within a time window.Yes. ["/api/users", id] and [id, "/api/users"] are different cache keys. SWR serializes array elements in order, so changing the order creates a different cache entry.
import { preload } from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
// Prefetch on hover
const handleMouseEnter = () => {
preload(`/api/products/${id}`, fetcher);
};const { cache } = useSWRConfig();
const handleLogout = () => {
if (cache instanceof Map) {
cache.clear();
}
};No. Object references are not serializable by SWR. Use strings or arrays as keys. useSWR({ url: "/api/users", id }, fetcher) will not cache properly.
The write silently fails. Implement error handling in your provider's set method using a try/catch, and consider evicting old entries or falling back to in-memory storage.
import type { Cache } from "swr";
const provider = (): Cache => {
const map = new Map();
return {
get: (key: string) => map.get(key),
set: (key: string, value: any) => map.set(key, value),
delete: (key: string) => map.delete(key),
keys: () => map.keys(),
};
};The provider option expects a function of type () => Cache. Import Cache from swr to type your custom implementation. Missing interface methods (keys, delete) will cause runtime errors.
Store entries with an expiry timestamp. On get, check if the entry has expired and return undefined if so. This forces SWR to treat it as a cache miss and re-fetch.
Reviewed by Chris St. John·Last updated Jul 7, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥