React Hooks library for remote data fetching with stale-while-revalidate caching strategy
—
The core useSWR hook provides comprehensive data fetching capabilities with caching, automatic revalidation, error handling, and loading states.
The primary hook for data fetching with automatic caching and revalidation.
/**
* Primary hook for data fetching with automatic caching and revalidation
* @param key - Unique identifier for the request (string, array, object, function, or falsy)
* @param fetcher - Function that fetches the data, or null to disable fetching
* @param config - Configuration options for this hook instance
* @returns SWRResponse object with data, error, loading states, and mutate function
*/
function useSWR<Data = any, Error = any>(
key: Key,
fetcher?: Fetcher<Data, Key> | null,
config?: SWRConfiguration<Data, Error>
): SWRResponse<Data, Error>;
// Overloads for different usage patterns
function useSWR<Data = any, Error = any>(
key: Key,
config?: SWRConfiguration<Data, Error>
): SWRResponse<Data, Error>;
function useSWR<Data = any, Error = any>(
fetcher: Fetcher<Data, Key>
): SWRResponse<Data, Error>;Usage Examples:
import useSWR from "swr";
// Basic usage with string key
const { data, error, isLoading } = useSWR("/api/user", fetcher);
// Using array key for multiple parameters
const { data } = useSWR(["/api/user", userId], ([url, id]) =>
fetch(`${url}/${id}`).then(res => res.json())
);
// Conditional fetching (key is falsy)
const { data } = useSWR(
shouldFetch ? "/api/data" : null,
fetcher
);
// With TypeScript generics
interface User {
id: number;
name: string;
email: string;
}
const { data, error } = useSWR<User, Error>("/api/user", fetcher);The return value from useSWR containing data, states, and utilities.
interface SWRResponse<Data, Error> {
/** The data returned by the fetcher (undefined if not loaded or error occurred) */
data: Data | undefined;
/** The error thrown by the fetcher (undefined if no error) */
error: Error | undefined;
/** Scoped mutate function for this specific key */
mutate: KeyedMutator<Data>;
/** True when a request or revalidation is loading */
isValidating: boolean;
/** True when there's no data and no error (initial loading) */
isLoading: boolean;
}SWR accepts various key types for different use cases.
type Key =
| string
| any[]
| object
| (() => string | any[] | object | null | undefined | false)
| null
| undefined
| false;Key Examples:
// String key
useSWR("/api/user", fetcher);
// Array key (for multiple parameters)
useSWR(["/api/user", userId, "profile"], fetcher);
// Object key
useSWR({ url: "/api/user", method: "GET" }, fetcher);
// Function key (dynamic)
useSWR(() => user ? `/api/user/${user.id}` : null, fetcher);
// Falsy key (disables fetching)
useSWR(null, fetcher); // won't fetch
useSWR(false, fetcher); // won't fetch
useSWR(undefined, fetcher); // won't fetchThe function responsible for fetching data based on the key.
type Fetcher<Data, SWRKey extends Key> = SWRKey extends () => infer Arg | null | undefined | false
? (arg: Arg) => Data | Promise<Data>
: SWRKey extends null | undefined | false
? never
: SWRKey extends infer Arg
? (arg: Arg) => Data | Promise<Data>
: never;
// Simplified common case
type BareFetcher<Data> = (...args: any[]) => Data | Promise<Data>;Fetcher Examples:
// Simple fetcher
const fetcher = (url: string) => fetch(url).then(res => res.json());
// Fetcher with error handling
const fetcher = async (url: string) => {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
return res.json();
};
// Fetcher for array keys
const fetcher = ([url, params]: [string, object]) =>
fetch(url, { method: "POST", body: JSON.stringify(params) })
.then(res => res.json());
// Fetcher with authentication
const fetcher = (url: string) =>
fetch(url, {
headers: {
Authorization: `Bearer ${getToken()}`
}
}).then(res => res.json());Comprehensive configuration options for customizing SWR behavior.
interface SWRConfiguration<Data = any, Error = any> {
/** Retry interval on error (default: 5000) */
errorRetryInterval?: number;
/** Max error retry count (default: 5) */
errorRetryCount?: number;
/** Timeout to trigger slow event (default: 3000) */
loadingTimeout?: number;
/** Focus revalidation throttle interval (default: 5000) */
focusThrottleInterval?: number;
/** Request deduplication interval (default: 2000) */
dedupingInterval?: number;
/** Polling interval (default: disabled) */
refreshInterval?: number;
/** Refresh when page is hidden (default: false) */
refreshWhenHidden?: boolean;
/** Refresh when offline (default: false) */
refreshWhenOffline?: boolean;
/** Revalidate on window focus (default: true) */
revalidateOnFocus?: boolean;
/** Revalidate on mount (default: undefined) */
revalidateOnMount?: boolean;
/** Revalidate on network reconnect (default: true) */
revalidateOnReconnect?: boolean;
/** Revalidate if data is stale (default: true) */
revalidateIfStale?: boolean;
/** Retry on error (default: true) */
shouldRetryOnError?: boolean | ((err: Error) => boolean);
/** Enable React Suspense mode (default: false) */
suspense?: boolean;
/** Initial data (default: undefined) */
fallbackData?: Data;
/** Keep previous data during revalidation (default: false) */
keepPreviousData?: boolean;
/** Custom data comparison function */
compare?: (a: Data | undefined, b: Data | undefined) => boolean;
/** Check if requests should be paused */
isPaused?: () => boolean;
/** Middleware functions */
use?: Middleware[];
/** Success callback */
onSuccess?: (data: Data, key: string, config: SWRConfiguration<Data, Error>) => void;
/** Error callback */
onError?: (err: Error, key: string, config: SWRConfiguration<Data, Error>) => void;
/** Custom error retry handler */
onErrorRetry?: (
err: Error,
key: string,
config: SWRConfiguration<Data, Error>,
revalidate: Revalidator,
revalidateOpts: Required<RevalidatorOptions>
) => void;
/** Slow loading callback */
onLoadingSlow?: (key: string, config: SWRConfiguration<Data, Error>) => void;
/** Fetcher function */
fetcher?: Fetcher<Data, Key> | null;
}Configuration Examples:
// Polling every 5 seconds
const { data } = useSWR("/api/stats", fetcher, {
refreshInterval: 5000
});
// Disable revalidation on focus
const { data } = useSWR("/api/static-data", fetcher, {
revalidateOnFocus: false
});
// Custom error retry logic
const { data } = useSWR("/api/data", fetcher, {
shouldRetryOnError: (error) => error.status !== 404,
errorRetryCount: 3,
errorRetryInterval: 1000
});
// With Suspense
const { data } = useSWR("/api/user", fetcher, {
suspense: true
});
// Keep previous data during revalidation
const { data } = useSWR(`/api/search?q=${query}`, fetcher, {
keepPreviousData: true
});Each useSWR instance returns a scoped mutate function for local cache updates.
interface KeyedMutator<Data> {
/**
* Mutate the cached data for this specific key
* @param data - New data, Promise, or function returning new data
* @param options - Mutation options or boolean for revalidate
* @returns Promise resolving to the new data
*/
(
data?: Data | Promise<Data> | MutatorCallback<Data>,
options?: boolean | MutatorOptions<Data>
): Promise<Data | undefined>;
}
type MutatorCallback<Data> = (currentData: Data | undefined) => Data | undefined;
interface MutatorOptions<Data = any> {
/** Whether to revalidate after mutation (default: true) */
revalidate?: boolean;
/** Whether to update cache with mutation result (default: true) */
populateCache?: boolean | ((result: Data, currentData: Data | undefined) => Data);
/** Data to show optimistically during mutation */
optimisticData?: Data | ((currentData: Data | undefined) => Data);
/** Whether to rollback on error (default: true) */
rollbackOnError?: boolean | ((error: any) => boolean);
}Mutate Examples:
const { data, mutate } = useSWR("/api/user", fetcher);
// Update with new data
await mutate({ ...data, name: "New Name" });
// Update without revalidation
await mutate(newData, false);
// Optimistic update
await mutate(
updateUser(newUserData),
{
optimisticData: { ...data, ...newUserData },
rollbackOnError: true
}
);
// Function-based update
await mutate((currentData) => ({
...currentData,
lastUpdated: Date.now()
}));Common patterns for using useSWR in complex scenarios.
Conditional Fetching:
function UserProfile({ userId }: { userId?: string }) {
const { data, error } = useSWR(
userId ? `/api/user/${userId}` : null,
fetcher
);
if (!userId) return <div>No user selected</div>;
if (error) return <div>Error loading user</div>;
if (!data) return <div>Loading...</div>;
return <div>{data.name}</div>;
}Dependent Requests:
function UserPosts({ userId }: { userId: string }) {
const { data: user } = useSWR(`/api/user/${userId}`, fetcher);
const { data: posts } = useSWR(
user ? `/api/user/${user.id}/posts` : null,
fetcher
);
return <div>{posts?.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}Global Error Handling:
function MyComponent() {
const { data, error } = useSWR("/api/data", fetcher, {
onError: (error, key) => {
console.error(`Error fetching ${key}:`, error);
// Global error reporting
errorReporter.captureException(error);
}
});
}Install with Tessl CLI
npx tessl i tessl/npm-swr