0
# Immutable Data
1
2
The `useSWRImmutable` hook is designed for static data that doesn't require revalidation, providing the same API as `useSWR` but with all revalidation options disabled.
3
4
## Capabilities
5
6
### useSWRImmutable Hook
7
8
Hook for static data that doesn't require revalidation, with all revalidation options disabled.
9
10
```typescript { .api }
11
/**
12
* Hook for static data that doesn't require revalidation
13
* @param key - Unique identifier for the request
14
* @param fetcher - Function that fetches the data, or null to disable fetching
15
* @param config - Configuration options (revalidation options are disabled)
16
* @returns SWRResponse object identical to useSWR
17
*/
18
function useSWRImmutable<Data = any, Error = any>(
19
key: Key,
20
fetcher?: Fetcher<Data, Key> | null,
21
config?: SWRConfiguration<Data, Error>
22
): SWRResponse<Data, Error>;
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import useSWRImmutable from "swr/immutable";
29
30
// Static configuration data
31
const { data: config } = useSWRImmutable("/api/config", fetcher);
32
33
// User profile (changes infrequently)
34
const { data: profile } = useSWRImmutable(
35
userId ? `/api/users/${userId}/profile` : null,
36
fetcher
37
);
38
39
// Application constants
40
const { data: constants } = useSWRImmutable("/api/constants", fetcher);
41
42
// Translation data
43
const { data: translations } = useSWRImmutable(
44
`/api/i18n/${locale}`,
45
fetcher
46
);
47
48
// One-time data fetch
49
const { data: report } = useSWRImmutable(
50
`/api/reports/${reportId}`,
51
fetcher
52
);
53
```
54
55
### Behavior Differences
56
57
`useSWRImmutable` automatically disables all revalidation options:
58
59
```typescript { .api }
60
// These options are automatically set to false:
61
const defaultConfig = {
62
revalidateOnFocus: false,
63
revalidateIfStale: false,
64
revalidateOnReconnect: false,
65
// All other options work normally
66
};
67
```
68
69
**Comparison with useSWR:**
70
71
```typescript
72
// Regular useSWR - will revalidate on focus, reconnect, etc.
73
const { data: dynamicData } = useSWR("/api/dynamic", fetcher);
74
75
// useSWRImmutable - will never revalidate automatically
76
const { data: staticData } = useSWRImmutable("/api/static", fetcher);
77
78
// Equivalent to useSWR with manual revalidation disabled
79
const { data: manualStatic } = useSWR("/api/static", fetcher, {
80
revalidateOnFocus: false,
81
revalidateIfStale: false,
82
revalidateOnReconnect: false,
83
});
84
```
85
86
### When to Use useSWRImmutable
87
88
**Perfect for:**
89
90
```typescript
91
// Application configuration
92
const { data: appConfig } = useSWRImmutable("/api/app-config", fetcher);
93
94
// Feature flags (when they don't change during session)
95
const { data: features } = useSWRImmutable("/api/feature-flags", fetcher);
96
97
// Static content (documentation, help text)
98
const { data: helpContent } = useSWRImmutable(
99
`/api/help/${section}`,
100
fetcher
101
);
102
103
// Cached computed results
104
const { data: expensiveComputation } = useSWRImmutable(
105
["compute", complexParams],
106
([, params]) => performExpensiveComputation(params)
107
);
108
109
// Historical data (doesn't change)
110
const { data: historicalData } = useSWRImmutable(
111
`/api/history/${date}`,
112
fetcher
113
);
114
115
// Reference data (currencies, countries, etc.)
116
const { data: currencies } = useSWRImmutable("/api/currencies", fetcher);
117
const { data: countries } = useSWRImmutable("/api/countries", fetcher);
118
```
119
120
### Manual Revalidation
121
122
Even with `useSWRImmutable`, you can still trigger manual revalidation:
123
124
```typescript
125
function StaticDataComponent() {
126
const { data, mutate } = useSWRImmutable("/api/config", fetcher);
127
128
// Manual refresh (still works)
129
const handleRefresh = () => {
130
mutate(); // This will revalidate even with useSWRImmutable
131
};
132
133
return (
134
<div>
135
<div>Config: {JSON.stringify(data)}</div>
136
<button onClick={handleRefresh}>Force Refresh</button>
137
</div>
138
);
139
}
140
141
// Global manual revalidation
142
import { mutate } from "swr";
143
144
const refreshStaticData = () => {
145
mutate("/api/config"); // Works even if using useSWRImmutable
146
};
147
```
148
149
### Advanced Patterns
150
151
**Conditional Immutability:**
152
153
```typescript
154
function DataComponent({ isStatic }: { isStatic: boolean }) {
155
// Choose hook based on data nature
156
const hook = isStatic ? useSWRImmutable : useSWR;
157
const { data, error } = hook("/api/data", fetcher);
158
159
return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
160
}
161
```
162
163
**Cache Warming:**
164
165
```typescript
166
function App() {
167
// Pre-load static data that will be needed throughout the app
168
useSWRImmutable("/api/config", fetcher);
169
useSWRImmutable("/api/user-permissions", fetcher);
170
useSWRImmutable("/api/feature-flags", fetcher);
171
172
return <Router />;
173
}
174
175
// Later components can access the cached data instantly
176
function FeatureComponent() {
177
const { data: features } = useSWRImmutable("/api/feature-flags", fetcher);
178
// This will return cached data immediately, no loading state
179
180
return features?.newFeature ? <NewFeature /> : <OldFeature />;
181
}
182
```
183
184
**Locale-Specific Data:**
185
186
```typescript
187
function LocalizedApp() {
188
const [locale, setLocale] = useState("en");
189
190
// Translation data is immutable per locale
191
const { data: translations } = useSWRImmutable(
192
`/api/i18n/${locale}`,
193
fetcher
194
);
195
196
// When locale changes, a new request is made but data for each locale
197
// is cached permanently (until page refresh)
198
199
return (
200
<div>
201
<select value={locale} onChange={(e) => setLocale(e.target.value)}>
202
<option value="en">English</option>
203
<option value="es">Spanish</option>
204
<option value="fr">French</option>
205
</select>
206
207
<div>{translations ? <TranslatedContent translations={translations} /> : "Loading..."}</div>
208
</div>
209
);
210
}
211
```
212
213
**Static Resource Loading:**
214
215
```typescript
216
// Hook for loading static resources
217
function useStaticResource<T>(path: string): T | undefined {
218
const { data } = useSWRImmutable(
219
path,
220
async (path: string) => {
221
const response = await fetch(path);
222
if (!response.ok) {
223
throw new Error(`Failed to load ${path}`);
224
}
225
return response.json();
226
}
227
);
228
229
return data;
230
}
231
232
// Usage
233
function DocumentationPage() {
234
const schema = useStaticResource<JsonSchema>("/schemas/api.json");
235
const examples = useStaticResource<Examples>("/examples/api-examples.json");
236
237
if (!schema || !examples) {
238
return <div>Loading documentation...</div>;
239
}
240
241
return <ApiDocumentation schema={schema} examples={examples} />;
242
}
243
```
244
245
**Performance Optimization:**
246
247
```typescript
248
// Use useSWRImmutable for expensive computations that don't change
249
function ExpensiveChart({ dataParams }: { dataParams: DataParams }) {
250
const { data: processedData } = useSWRImmutable(
251
["processed-data", dataParams],
252
([, params]) => {
253
// This expensive computation will only run once per unique params
254
return processLargeDataset(params);
255
}
256
);
257
258
return <Chart data={processedData} />;
259
}
260
261
// Heavy external API calls for static data
262
function CountryInfo({ countryCode }: { countryCode: string }) {
263
const { data: countryDetails } = useSWRImmutable(
264
`country-${countryCode}`,
265
() => fetchCountryFromExternalAPI(countryCode), // Expensive external call
266
{
267
// Even though it's immutable, we might want error retry
268
shouldRetryOnError: true,
269
errorRetryInterval: 1000,
270
}
271
);
272
273
return <div>{countryDetails?.name}</div>;
274
}
275
```
276
277
### Best Practices
278
279
**When to choose useSWRImmutable over useSWR:**
280
281
1. **Static Configuration**: App config, feature flags, constants
282
2. **Reference Data**: Countries, currencies, time zones
283
3. **Historical Data**: Past reports, archived content
284
4. **Heavy Computations**: Results that are expensive to recalculate
285
5. **One-time Fetches**: Data that definitely won't change during session
286
6. **Static Content**: Help docs, terms of service, static pages
287
288
**When to stick with useSWR:**
289
290
1. **User Data**: Profiles, preferences (even if updated infrequently)
291
2. **Live Data**: Anything that might change during user session
292
3. **Collaborative Data**: Content that other users might modify
293
4. **Time-Sensitive Data**: Even if updates are rare
294
5. **Error Recovery**: When you need automatic revalidation on reconnect
295
296
**Performance Considerations:**
297
298
```typescript
299
// Good: Static data that never changes
300
const { data: constants } = useSWRImmutable("/api/constants", fetcher);
301
302
// Good: Heavy computation with stable inputs
303
const { data: result } = useSWRImmutable(
304
["heavy-calc", stableParams],
305
computeExpensiveResult
306
);
307
308
// Avoid: Data that might change (use useSWR instead)
309
// const { data: userNotifications } = useSWRImmutable("/api/notifications", fetcher);
310
311
// Avoid: External APIs that might return different data
312
// const { data: weather } = useSWRImmutable("/api/weather", fetcher);
313
```