0
# Cache Management
1
2
SWR provides powerful cache management capabilities through global mutate functions and preloading utilities for programmatic cache manipulation and data optimization.
3
4
## Capabilities
5
6
### Global Mutate Function
7
8
Global function for programmatic cache manipulation across all SWR instances.
9
10
```typescript { .api }
11
/**
12
* Global mutate function for cache manipulation
13
* @param key - Key to mutate, or function to match multiple keys
14
* @param data - New data, Promise, or function returning new data
15
* @param options - Mutation options or boolean for revalidate
16
* @returns Promise resolving to the updated data
17
*/
18
function mutate<Data = any>(
19
key: Key | ((key: Key) => boolean),
20
data?: Data | Promise<Data> | MutatorCallback<Data>,
21
options?: boolean | MutatorOptions<Data>
22
): Promise<Data | undefined>;
23
24
// Overload for mutating without providing data (triggers revalidation)
25
function mutate(
26
key: Key | ((key: Key) => boolean),
27
options?: boolean | MutatorOptions
28
): Promise<any>;
29
```
30
31
**Usage Examples:**
32
33
```typescript
34
import { mutate } from "swr";
35
36
// Update specific cache entry
37
await mutate("/api/user", newUserData);
38
39
// Revalidate specific key
40
await mutate("/api/user");
41
42
// Update multiple related keys
43
await mutate((key) => typeof key === "string" && key.startsWith("/api/users"));
44
45
// Optimistic update with rollback
46
await mutate(
47
"/api/user",
48
updateUserAPI(newData),
49
{
50
optimisticData: { ...currentData, ...newData },
51
rollbackOnError: true
52
}
53
);
54
```
55
56
### Preload Function
57
58
Function to preload data into cache before it's needed by components.
59
60
```typescript { .api }
61
/**
62
* Preload data into cache before components need it
63
* @param key - Cache key for the data
64
* @param fetcher - Function to fetch the data
65
* @returns The fetched data
66
*/
67
function preload<Data = any>(
68
key: Key,
69
fetcher: Fetcher<Data, Key>
70
): Data;
71
```
72
73
**Usage Examples:**
74
75
```typescript
76
import { preload } from "swr";
77
78
// Preload critical data on app start
79
preload("/api/user", fetcher);
80
preload("/api/config", fetcher);
81
82
// Preload on hover (before navigation)
83
const handleLinkHover = () => {
84
preload("/api/dashboard", fetcher);
85
};
86
87
// Preload related data after successful mutation
88
const handleUserUpdate = async (userData) => {
89
await mutate("/api/user", userData);
90
91
// Preload related data that user might need next
92
preload("/api/user/preferences", fetcher);
93
preload("/api/user/activity", fetcher);
94
};
95
```
96
97
### Cache Inspection and Management
98
99
Access and manipulate the cache directly for advanced use cases.
100
101
```typescript
102
import { useSWRConfig } from "swr";
103
104
function CacheManager() {
105
const { cache, mutate } = useSWRConfig();
106
107
// Inspect cache contents
108
const logCache = () => {
109
console.log("Cache contents:", cache);
110
111
// Get all cached keys
112
const keys = Array.from(cache.keys());
113
console.log("Cached keys:", keys);
114
115
// Get specific cache entry
116
const userData = cache.get("/api/user");
117
console.log("User data:", userData);
118
};
119
120
// Clear entire cache
121
const clearCache = () => {
122
cache.clear();
123
};
124
125
// Remove specific cache entry
126
const removeCacheEntry = (key: string) => {
127
cache.delete(key);
128
};
129
130
// Batch cache operations
131
const batchCacheUpdate = () => {
132
const updates = [
133
["/api/user", newUserData],
134
["/api/settings", newSettings],
135
["/api/preferences", newPreferences]
136
];
137
138
updates.forEach(([key, data]) => {
139
mutate(key, data, false); // Update without revalidation
140
});
141
};
142
143
return (
144
<div>
145
<button onClick={logCache}>Log Cache</button>
146
<button onClick={clearCache}>Clear Cache</button>
147
<button onClick={batchCacheUpdate}>Batch Update</button>
148
</div>
149
);
150
}
151
```
152
153
### Advanced Mutation Patterns
154
155
**Pattern Matching for Bulk Updates:**
156
157
```typescript
158
// Update all user-related cache entries
159
await mutate(
160
(key) => typeof key === "string" && key.includes("/api/user"),
161
undefined, // No data provided, will revalidate
162
{ revalidate: true }
163
);
164
165
// Update cache entries matching specific pattern
166
await mutate(
167
(key) => {
168
if (typeof key === "string") {
169
return key.startsWith("/api/posts") || key.startsWith("/api/comments");
170
}
171
return false;
172
}
173
);
174
175
// Complex key matching with arrays
176
await mutate(
177
(key) => {
178
if (Array.isArray(key)) {
179
return key[0] === "/api/search" && key[1] === currentQuery;
180
}
181
return false;
182
}
183
);
184
```
185
186
**Conditional Cache Updates:**
187
188
```typescript
189
// Update cache only if data has changed
190
const updateUserIfChanged = async (newUserData: User) => {
191
await mutate(
192
"/api/user",
193
newUserData,
194
{
195
populateCache: (result, currentData) => {
196
// Only update if data actually changed
197
if (JSON.stringify(result) !== JSON.stringify(currentData)) {
198
return result;
199
}
200
return currentData; // Keep existing data
201
},
202
revalidate: false
203
}
204
);
205
};
206
207
// Conditional optimistic updates
208
const optimisticUpdate = async (action: () => Promise<any>) => {
209
const shouldBeOptimistic = checkNetworkCondition();
210
211
if (shouldBeOptimistic) {
212
await mutate(
213
"/api/data",
214
action(),
215
{
216
optimisticData: (current) => ({ ...current, updating: true }),
217
rollbackOnError: true
218
}
219
);
220
} else {
221
// Just do the action without optimistic update
222
await action();
223
await mutate("/api/data"); // Revalidate after
224
}
225
};
226
```
227
228
### Cache Warming Strategies
229
230
**Application Startup:**
231
232
```typescript
233
// Warm cache with essential data on app load
234
const warmCache = async () => {
235
const essentialData = [
236
["/api/user", userFetcher],
237
["/api/config", configFetcher],
238
["/api/permissions", permissionsFetcher]
239
];
240
241
// Preload all essential data in parallel
242
await Promise.all(
243
essentialData.map(([key, fetcher]) => preload(key, fetcher))
244
);
245
};
246
247
// Call during app initialization
248
warmCache();
249
```
250
251
**Predictive Loading:**
252
253
```typescript
254
// Preload data based on user behavior
255
const handleUserInteraction = (action: string) => {
256
switch (action) {
257
case "hover_profile":
258
preload("/api/user/profile", fetcher);
259
preload("/api/user/activity", fetcher);
260
break;
261
262
case "hover_dashboard":
263
preload("/api/dashboard/stats", fetcher);
264
preload("/api/dashboard/notifications", fetcher);
265
break;
266
267
case "search_focus":
268
preload("/api/search/recent", fetcher);
269
preload("/api/search/suggestions", fetcher);
270
break;
271
}
272
};
273
274
// Usage in components
275
<button
276
onMouseEnter={() => handleUserInteraction("hover_dashboard")}
277
onClick={navigateToDashboard}
278
>
279
Dashboard
280
</button>
281
```
282
283
**Route-Based Preloading:**
284
285
```typescript
286
// Preload data for upcoming routes
287
const preloadRoute = (routeName: string) => {
288
const routePreloads = {
289
dashboard: [
290
"/api/dashboard/stats",
291
"/api/dashboard/recent-activity"
292
],
293
profile: [
294
"/api/user/profile",
295
"/api/user/settings"
296
],
297
reports: [
298
"/api/reports/list",
299
"/api/reports/filters"
300
]
301
};
302
303
const keys = routePreloads[routeName] || [];
304
keys.forEach(key => preload(key, fetcher));
305
};
306
307
// Integration with router
308
const router = useRouter();
309
310
useEffect(() => {
311
const handleRouteChangeStart = (url: string) => {
312
const routeName = url.split("/")[1];
313
preloadRoute(routeName);
314
};
315
316
router.events.on("routeChangeStart", handleRouteChangeStart);
317
return () => router.events.off("routeChangeStart", handleRouteChangeStart);
318
}, [router]);
319
```
320
321
### Cache Invalidation Strategies
322
323
**Time-Based Invalidation:**
324
325
```typescript
326
// Invalidate cache entries older than threshold
327
const invalidateStaleCache = () => {
328
const STALE_TIME = 5 * 60 * 1000; // 5 minutes
329
const now = Date.now();
330
331
mutate((key) => {
332
// Get cache entry to check timestamp
333
const entry = cache.get(key);
334
if (entry && entry.timestamp) {
335
return now - entry.timestamp > STALE_TIME;
336
}
337
return false;
338
});
339
};
340
341
// Run periodically
342
setInterval(invalidateStaleCache, 60000); // Every minute
343
```
344
345
**Event-Based Invalidation:**
346
347
```typescript
348
// Invalidate cache based on external events
349
const handleExternalEvent = (event: ExternalEvent) => {
350
switch (event.type) {
351
case "user_updated":
352
// Invalidate all user-related data
353
mutate((key) => typeof key === "string" && key.includes("/api/user"));
354
break;
355
356
case "permissions_changed":
357
// Invalidate permission-related data
358
mutate("/api/permissions");
359
mutate("/api/user/permissions");
360
break;
361
362
case "global_config_changed":
363
// Invalidate configuration data
364
mutate((key) => typeof key === "string" && key.includes("/api/config"));
365
break;
366
}
367
};
368
369
// Listen for external events (WebSocket, SSE, etc.)
370
websocket.on("event", handleExternalEvent);
371
```
372
373
**Smart Invalidation:**
374
375
```typescript
376
// Intelligent cache invalidation based on data relationships
377
const smartInvalidate = async (changedData: any, context: string) => {
378
const invalidationMap = {
379
user_profile: [
380
"/api/user",
381
"/api/user/profile",
382
(key) => typeof key === "string" && key.startsWith("/api/user/")
383
],
384
user_settings: [
385
"/api/user/settings",
386
"/api/user/preferences"
387
],
388
post_created: [
389
"/api/posts",
390
(key) => Array.isArray(key) && key[0] === "/api/posts" && key[1] === "list"
391
]
392
};
393
394
const toInvalidate = invalidationMap[context] || [];
395
396
for (const target of toInvalidate) {
397
if (typeof target === "function") {
398
await mutate(target);
399
} else {
400
await mutate(target);
401
}
402
}
403
};
404
```
405
406
### Performance Optimization
407
408
**Batch Cache Operations:**
409
410
```typescript
411
// Batch multiple cache operations for better performance
412
const batchCacheUpdates = async (updates: Array<[string, any]>) => {
413
// Disable revalidation for all updates except the last one
414
const promises = updates.map(([key, data], index) =>
415
mutate(key, data, { revalidate: index === updates.length - 1 })
416
);
417
418
await Promise.all(promises);
419
};
420
421
// Usage
422
await batchCacheUpdates([
423
["/api/user", newUserData],
424
["/api/settings", newSettings],
425
["/api/preferences", newPreferences]
426
]);
427
```
428
429
**Memory Management:**
430
431
```typescript
432
// Clean up cache to prevent memory leaks
433
const cleanupCache = () => {
434
const { cache } = useSWRConfig();
435
const MAX_CACHE_SIZE = 100;
436
437
if (cache.size > MAX_CACHE_SIZE) {
438
// Remove oldest entries (this is a simplified example)
439
const entries = Array.from(cache.entries());
440
const oldestEntries = entries
441
.sort((a, b) => (a[1].timestamp || 0) - (b[1].timestamp || 0))
442
.slice(0, entries.length - MAX_CACHE_SIZE);
443
444
oldestEntries.forEach(([key]) => cache.delete(key));
445
}
446
};
447
448
// Run cleanup periodically
449
useEffect(() => {
450
const interval = setInterval(cleanupCache, 60000); // Every minute
451
return () => clearInterval(interval);
452
}, []);
453
```
454
455
**Cache Debugging:**
456
457
```typescript
458
// Debugging utilities for cache management
459
const useCacheDebugger = () => {
460
const { cache, mutate } = useSWRConfig();
461
462
const debugCache = {
463
log: () => {
464
console.group("SWR Cache Debug");
465
console.log("Cache size:", cache.size);
466
console.log("Keys:", Array.from(cache.keys()));
467
console.groupEnd();
468
},
469
470
logKey: (key: string) => {
471
const entry = cache.get(key);
472
console.log(`Cache entry for ${key}:`, entry);
473
},
474
475
stats: () => {
476
const keys = Array.from(cache.keys());
477
const typeStats = keys.reduce((acc, key) => {
478
const type = typeof key === "string" ? "string" :
479
Array.isArray(key) ? "array" : "object";
480
acc[type] = (acc[type] || 0) + 1;
481
return acc;
482
}, {} as Record<string, number>);
483
484
console.log("Cache statistics:", typeStats);
485
},
486
487
export: () => {
488
const cacheData = {};
489
cache.forEach((value, key) => {
490
cacheData[JSON.stringify(key)] = value;
491
});
492
return cacheData;
493
}
494
};
495
496
return debugCache;
497
};
498
499
// Usage in development
500
const debugCache = useCacheDebugger();
501
debugCache.log(); // Log current cache state
502
```