CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-swr

React Hooks library for remote data fetching with stale-while-revalidate caching strategy

Pending
Overview
Eval results
Files

core-data-fetching.mddocs/

Core Data Fetching

The core useSWR hook provides comprehensive data fetching capabilities with caching, automatic revalidation, error handling, and loading states.

Capabilities

useSWR Hook

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);

SWR Response Object

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;
}

Key Types

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 fetch

Fetcher Function

The 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());

Configuration Options

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
});

Scoped Mutate Function

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()
}));

Advanced Hook Patterns

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

docs

cache-management.md

core-data-fetching.md

global-configuration.md

immutable-data.md

index.md

infinite-loading.md

mutations.md

subscriptions.md

tile.json