React Hooks library for remote data fetching with stale-while-revalidate caching strategy
—
The SWRConfig component provides global configuration for all SWR hooks within its scope, enabling centralized management of fetchers, error handling, and other options.
Provider component for configuring SWR behavior globally across the application.
/**
* Provider component for global SWR configuration
* @param props.value - Partial configuration object to apply globally
* @param props.children - Child components that will inherit the configuration
*/
interface SWRConfig {
(props: {
value?: Partial<SWRConfiguration>;
children: React.ReactNode;
}): JSX.Element;
/** Default configuration values */
defaultValue: SWRConfiguration;
}Usage Examples:
import { SWRConfig } from "swr";
// Basic global configuration
function App() {
return (
<SWRConfig
value={{
fetcher: (url: string) => fetch(url).then(res => res.json()),
onError: (error) => {
console.error('SWR Error:', error);
}
}}
>
<MyComponents />
</SWRConfig>
);
}
// Advanced configuration with multiple options
function App() {
return (
<SWRConfig
value={{
fetcher: customFetcher,
revalidateOnFocus: false,
revalidateOnReconnect: true,
errorRetryCount: 3,
errorRetryInterval: 1000,
onError: handleGlobalError,
onSuccess: handleGlobalSuccess,
shouldRetryOnError: (error) => error.status >= 500,
}}
>
<Router />
</SWRConfig>
);
}Hook to access the current SWR configuration and global mutate function.
/**
* Hook to access current SWR configuration and global utilities
* @returns Object containing cache, mutate function, and current configuration
*/
function useSWRConfig(): {
cache: Cache;
mutate: (key?: Key, data?: any, options?: boolean | MutatorOptions) => Promise<any>;
[key: string]: any;
};Usage Examples:
import { useSWRConfig } from "swr";
function MyComponent() {
const { mutate, cache } = useSWRConfig();
// Global cache mutation
const handleInvalidateAll = () => {
mutate(() => true); // Revalidate all keys
};
// Specific cache mutation
const handleUpdateUser = (newUser: User) => {
mutate(`/api/users/${newUser.id}`, newUser, false);
};
// Cache inspection (for debugging)
const handleLogCache = () => {
console.log("Current cache:", cache);
};
return (
<div>
<button onClick={handleInvalidateAll}>Refresh All Data</button>
<button onClick={handleLogCache}>Log Cache</button>
</div>
);
}SWR configurations can be nested, with inner configurations overriding outer ones.
function App() {
return (
<SWRConfig
value={{
fetcher: globalFetcher,
revalidateOnFocus: false,
}}
>
<Header />
{/* Admin section with different configuration */}
<SWRConfig
value={{
fetcher: adminFetcher, // Overrides global fetcher
revalidateOnFocus: true, // Overrides global setting
onError: adminErrorHandler,
}}
>
<AdminPanel />
</SWRConfig>
<MainContent />
</SWRConfig>
);
}Configure global error handling for all SWR hooks.
function App() {
return (
<SWRConfig
value={{
onError: (error, key) => {
// Global error logging
console.error(`Error for key ${key}:`, error);
// Report to error tracking service
errorTracker.captureException(error, { extra: { key } });
// Show user-friendly error messages
if (error.status === 401) {
// Redirect to login
window.location.href = "/login";
} else if (error.status >= 500) {
toast.error("Server error. Please try again later.");
} else if (error.status === 404) {
toast.error("Resource not found.");
}
},
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
// Don't retry on 404
if (error.status === 404) return;
// Don't retry on authentication errors
if (error.status === 401) return;
// Only retry up to 3 times
if (retryCount >= 3) return;
// Exponential backoff
setTimeout(() => revalidate({ retryCount }), 2 ** retryCount * 1000);
}
}}
>
<App />
</SWRConfig>
);
}Configure global success callbacks for all SWR hooks.
function App() {
return (
<SWRConfig
value={{
onSuccess: (data, key) => {
// Global success logging
console.log(`Successfully loaded ${key}`);
// Analytics tracking
analytics.track("data_loaded", { key });
// Cache warming for related data
if (key.includes("/api/user/")) {
// Pre-load user's related data
const userId = key.split("/").pop();
mutate(`/api/user/${userId}/preferences`);
}
}
}}
>
<App />
</SWRConfig>
);
}Set up a global fetcher with authentication, error handling, and request configuration.
// Global fetcher with authentication and error handling
const globalFetcher = async (url: string) => {
const token = getAuthToken();
const response = await fetch(url, {
headers: {
"Content-Type": "application/json",
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (!response.ok) {
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
(error as any).status = response.status;
(error as any).response = response;
throw error;
}
return response.json();
};
function App() {
return (
<SWRConfig value={{ fetcher: globalFetcher }}>
<Router />
</SWRConfig>
);
}
// Advanced fetcher with request interceptors
const advancedFetcher = async (url: string | [string, RequestInit]) => {
const [actualUrl, options = {}] = Array.isArray(url) ? url : [url, {}];
// Add default headers
const headers = {
"Content-Type": "application/json",
...options.headers,
};
// Add authentication
const token = getAuthToken();
if (token) {
headers.Authorization = `Bearer ${token}`;
}
// Make request
const response = await fetch(actualUrl, {
...options,
headers,
});
// Handle different response types
const contentType = response.headers.get("content-type");
if (!response.ok) {
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
// Try to get more specific error from response body
try {
if (contentType?.includes("application/json")) {
const errorData = await response.json();
errorMessage = errorData.message || errorMessage;
}
} catch {}
const error = new Error(errorMessage);
(error as any).status = response.status;
throw error;
}
// Return appropriate data format
if (contentType?.includes("application/json")) {
return response.json();
} else if (contentType?.includes("text/")) {
return response.text();
} else {
return response.blob();
}
};Configure SWR for optimal performance based on your application needs.
function App() {
return (
<SWRConfig
value={{
// Reduce revalidation frequency for better performance
dedupingInterval: 5000, // 5 seconds
focusThrottleInterval: 10000, // 10 seconds
// Disable revalidation for better perceived performance
revalidateOnFocus: false,
revalidateOnReconnect: true,
// Optimize error retry
errorRetryCount: 2,
errorRetryInterval: 2000,
shouldRetryOnError: (error) => {
// Only retry on network errors and 5xx responses
return !error.status || error.status >= 500;
},
// Keep data fresh but reduce network requests
refreshInterval: 60000, // 1 minute polling for critical data
refreshWhenHidden: false,
refreshWhenOffline: false,
// Improve loading experience
keepPreviousData: true,
loadingTimeout: 3000,
onLoadingSlow: (key) => {
console.warn(`Slow loading detected for: ${key}`);
// Could show a loading indicator or warning
}
}}
>
<App />
</SWRConfig>
);
}Configure SWR differently based on environment.
const getConfig = () => {
const baseConfig = {
fetcher: globalFetcher,
};
if (process.env.NODE_ENV === "development") {
return {
...baseConfig,
// More aggressive revalidation in development
revalidateOnFocus: true,
revalidateOnReconnect: true,
// Shorter intervals for testing
dedupingInterval: 1000,
errorRetryInterval: 500,
// Detailed logging
onError: (error, key) => {
console.group(`SWR Error: ${key}`);
console.error(error);
console.groupEnd();
},
onSuccess: (data, key) => {
console.log(`SWR Success: ${key}`, data);
}
};
}
if (process.env.NODE_ENV === "production") {
return {
...baseConfig,
// Conservative revalidation in production
revalidateOnFocus: false,
revalidateOnReconnect: true,
// Longer intervals for better performance
dedupingInterval: 10000,
errorRetryInterval: 5000,
// Error reporting only
onError: (error, key) => {
errorReporter.captureException(error, { extra: { key } });
}
};
}
return baseConfig;
};
function App() {
return (
<SWRConfig value={getConfig()}>
<Router />
</SWRConfig>
);
}Update SWR configuration based on application state.
function ConfigurableApp() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
const [userPreferences, setUserPreferences] = useState(null);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);
const swrConfig = useMemo(() => ({
fetcher: globalFetcher,
// Adjust behavior based on connection
revalidateOnReconnect: isOnline,
refreshWhenOffline: false,
errorRetryCount: isOnline ? 3 : 0,
// Adjust based on user preferences
revalidateOnFocus: userPreferences?.autoRefresh ?? true,
refreshInterval: userPreferences?.pollInterval ?? 0,
// Custom pause logic
isPaused: () => {
// Pause requests when offline or user disabled auto-refresh
return !isOnline || userPreferences?.pauseRequests;
}
}), [isOnline, userPreferences]);
return (
<SWRConfig value={swrConfig}>
<App />
</SWRConfig>
);
}Install with Tessl CLI
npx tessl i tessl/npm-swr