Skip to content

/web-data-fetching-swr

SWR data fetching patterns - useSWR, useSWRMutation, caching, revalidation, infinite scroll

shell
$ npx -y skills add agents-inc/skills --skill web-data-fetching-swr --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/web-data-fetching-swr
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

SWR data fetching patterns - useSWR, useSWRMutation, caching, revalidation, infinite scroll

SKILL.md

web-data-fetching-swr.SKILL.md
name: web-data-fetching-swr
description: SWR data fetching patterns - useSWR, useSWRMutation, caching, revalidation, infinite scroll

SWR Data Fetching Patterns

> **Quick Guide:** SWR implements the stale-while-revalidate caching strategy: show cached data instantly, revalidate in the background. Keys must be stable (strings or stable arrays), `isLoading` is for initial fetches only (use `isValidating` for background refreshes), and all write operations go through `useSWRMutation`. The null key pattern is how you do conditional fetching -- never call hooks conditionally.

---

<critical_requirements>

CRITICAL: Before Using This Skill

**(You MUST use a stable key -- keys should NOT change on every render or you'll trigger infinite requests)**

**(You MUST handle isLoading vs isValidating correctly -- isLoading is true only on initial fetch with no data)**

**(You MUST wrap mutations in `useSWRMutation` for write operations -- NOT useSWR)**

**(You MUST use named constants for ALL timeout, retry, and interval values -- NO magic numbers)**

**(You MUST use named exports only -- NO default exports)**

</critical_requirements>

---

**Auto-detection:** SWR, useSWR, useSWRMutation, useSWRInfinite, useSWRImmutable, SWRConfig, mutate, revalidate, fetcher, stale-while-revalidate, preload

**When to use:**

  • Read-heavy applications with infrequent mutations
  • Need lightweight bundle (~5KB gzipped)
  • Simple caching with automatic revalidation
  • Applications where stale-while-revalidate pattern is desired

**When NOT to use:**

  • Complex mutation workflows requiring many lifecycle callbacks
  • Need built-in request cancellation (SWR requires manual AbortController)
  • Complex dependent queries needing fine-grained invalidation control

**Key patterns covered:**

  • useSWR hook with typed fetchers and state handling
  • isLoading vs isValidating distinction (the most common mistake)
  • Revalidation strategies (focus, reconnect, interval, manual)
  • useSWRMutation for write operations with optimistic updates
  • useSWRInfinite for cursor and offset pagination
  • Null key pattern for conditional fetching
  • SWRConfig for global defaults and SSR fallback

**Detailed Resources:**

  • [examples/core.md](examples/core.md) -- Fetchers, return values, SWRConfig, key patterns
  • [examples/mutations.md](examples/mutations.md) -- useSWRMutation, optimistic updates, cache invalidation
  • [examples/caching.md](examples/caching.md) -- Revalidation strategies, prefetching, persistence
  • [examples/pagination.md](examples/pagination.md) -- useSWRInfinite, infinite scroll, offset pagination
  • [examples/conditional.md](examples/conditional.md) -- Dependent queries, auth-gated fetching
  • [examples/error-handling.md](examples/error-handling.md) -- Retry config, error boundaries, network detection
  • [examples/suspense.md](examples/suspense.md) -- Suspense integration, SSR fallback patterns
  • [reference.md](reference.md) -- Decision frameworks, configuration tables

---

<philosophy>

Philosophy

SWR (stale-while-revalidate) returns cached data first, then revalidates in the background. This creates fast, responsive UIs while ensuring data freshness.

**Core principles:**

  • **Stale-While-Revalidate**: Show cached data immediately, update in background
  • **Deduplication**: Multiple components using same key share one request
  • **Focus Revalidation**: Refetch when user returns to tab
  • **Optimistic UI**: Update UI immediately, rollback on error
  • **Minimal API**: Simple hooks, less configuration than alternatives

**Trade-offs:**

  • Simpler API means less control over complex mutation scenarios
  • Request cancellation requires manual AbortController setup
  • Less opinionated about mutations (fewer lifecycle callbacks)

</philosophy>

---

<patterns>

Core Patterns

Pattern 1: Typed Fetcher

The fetcher must throw on non-OK responses. If it doesn't throw, SWR treats error bodies as valid data.

// lib/fetcher.ts
interface FetchError extends Error {
  info: unknown;
  status: number;
}

const fetcher = async <T>(url: string): Promise<T> => {
  const response = await fetch(url);
  if (!response.ok) {
    const error = new Error("Fetch failed") as FetchError;
    error.info = await response.json().catch(() => null);
    error.status = response.status;
    throw error;
  }
  return response.json();
};

export { fetcher };
export type { FetchError };

**Why good:** Throws on error (required for SWR error state to work), attaches status for conditional handling, typed error enables downstream type narrowing

See [examples/core.md](examples/core.md) for axios, GraphQL, and multi-argument fetcher variants.

---

Pattern 2: isLoading vs isValidating

The most common SWR mistake. `isLoading` is true only on initial fetch with no data. `isValidating` is true during any in-flight request.

// State combinations:
// Initial load:     { data: undefined, isLoading: true,  isValidating: true }
// Success:          { data: T,         isLoading: false, isValidating: false }
// Revalidating:     { data: T,         isLoading: false, isValidating: true }
// Error (no data):  { error: Error,    isLoading: false, isValidating: false }
// Error (has data): { data: T, error: Error, isLoading: false }
// BAD: Using isValidating as loading indicator hides cached data
if (isValidating) return <Spinner />;

// GOOD: isLoading for initial, isValidating for refresh indicator
if (isLoading) return <Spinner />;
return (
  <div>
    {isValidating && <RefreshIndicator />}
    {error && data && <Banner>Data may be outdated</Banner>}
    <Content data={data} />
  </div>
);

**Why bad:** Showing spinner during background revalidation hides perfectly valid cached data, defeating the purpose of stale-while-revalidate

See [examples/core.md](examples/core.md) for full state handling with error + stale data combinations.

---

Pattern 3: SWRConfig Global Defaults

Centralize fetcher, retry, an

Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin, auto-invoked