SWR: React Data Fetching & Caching

VerifiedSafe

Fetch, cache, and revalidate data in React with SWR, a lightweight library by Vercel. Supports pagination, optimistic updates, and background revalidation.

Sby Skills Guide Bot
DevelopmentIntermediate
7/28/2026
Claude CodeCursorCopilot
#react#data-fetching#swr#caching#optimistic-updates

Recommended for

Automated summary

Explains how to fetch, cache, and revalidate React data with SWR, covering mutations, pagination, and optimistic updates.

Strengths

  • Copy-paste-ready snippets for the fetching hook, global config, and mutations
  • Shows optimistic updates using optimisticData with rollbackOnError
  • Covers pagination and infinite loading driven by the useSWR key
  • Pragmatic, small-API approach with revalidation options explained

Limitations

  • The file appears truncated (the "Infinite Loading" section cuts off mid-snippet)
  • Does not address testing, error boundaries, or detailed error handling
  • Assumes a fetch-based JSON fetcher and never actually compares against TanStack Query
When to use it

Use it when a React component needs cached API data that refreshes itself in the background.

When not to use it

Skip it if the project isn't React or you need advanced capabilities like offline persistence and devtools from a fuller query library.

Security analysis

Safe
Quality score92/100

This is an educational skill about the SWR library for React data fetching. It contains standard code examples and instructions using npm install and fetch, with no destructive, exfiltrating, or obfuscated actions. There are no declared tools that would execute code, and the content poses no meaningful execution risk.

No concerns found

How this analysis is made

Examples

Basic hook + loading state
Add SWR to my Next.js app: create a useUser hook that fetches /api/users/[id], exposes isLoading and an error, and lets me manually revalidate.
Optimistic todo creation
Using SWR in my React app, implement optimistic updates for adding a todo with mutate, optimisticData, and rollbackOnError so the list reverts if the POST fails.
Global SWR config
Set up a global SWRConfig in my Vite React app with a shared fetcher that throws on non-OK responses, revalidateOnFocus, and a 2000ms dedupingInterval.

name: swr description: >- Fetch, cache, and revalidate data in React with SWR — stale-while-revalidate data fetching library by Vercel. Use when someone asks to "fetch data in React", "SWR", "data fetching hook", "cache API calls", "stale-while- revalidate", "auto-refresh data", or "React data fetching without Redux". Covers data fetching, caching, revalidation, mutation, pagination, and optimistic updates. license: Apache-2.0 compatibility: "React 18+. Next.js, Remix, Vite." metadata: author: terminal-skills version: "1.0.0" category: development tags: ["data-fetching", "swr", "react", "cache", "revalidation"]

SWR

Overview

SWR (stale-while-revalidate) is a React data fetching library — show cached data instantly, then revalidate in the background. Built by Vercel, it handles caching, deduplication, revalidation on focus/reconnect, pagination, and optimistic updates. Simpler than TanStack Query for straightforward data fetching, with a smaller API surface.

When to Use

  • Fetching API data in React components
  • Need instant page loads with background revalidation
  • Real-time data that should refresh automatically
  • Paginated or infinite scroll data
  • Simpler alternative to TanStack Query for most use cases

Instructions

Setup

npm install swr

Basic Data Fetching

// hooks/useUser.ts — Fetch and cache user data
import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then((r) => r.json());

export function useUser(userId: string) {
  const { data, error, isLoading, mutate } = useSWR(
    `/api/users/${userId}`,
    fetcher,
  );

  return {
    user: data,
    isLoading,
    isError: error,
    mutate,  // Manually revalidate
  };
}

// Usage in component
function UserProfile({ userId }) {
  const { user, isLoading } = useUser(userId);
  if (isLoading) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

Global Configuration

// app/providers.tsx — Global SWR config
import { SWRConfig } from "swr";

const fetcher = async (url: string) => {
  const res = await fetch(url);
  if (!res.ok) throw new Error("API error");
  return res.json();
};

export function Providers({ children }) {
  return (
    <SWRConfig
      value={{
        fetcher,
        revalidateOnFocus: true,      // Refresh when tab regains focus
        revalidateOnReconnect: true,   // Refresh when internet reconnects
        dedupingInterval: 2000,        // Dedupe requests within 2s
        errorRetryCount: 3,
      }}
    >
      {children}
    </SWRConfig>
  );
}

Mutation and Optimistic Updates

// components/TodoList.tsx — Optimistic updates
import useSWR, { useSWRConfig } from "swr";

function TodoList() {
  const { data: todos, mutate } = useSWR("/api/todos");

  const addTodo = async (title: string) => {
    const newTodo = { id: Date.now(), title, done: false };

    // Optimistic update — show immediately, revalidate in background
    await mutate(
      async () => {
        await fetch("/api/todos", {
          method: "POST",
          body: JSON.stringify({ title }),
        });
        // Return updated data (or let SWR refetch)
      },
      {
        optimisticData: [...(todos || []), newTodo],
        rollbackOnError: true,  // Revert if API fails
        revalidate: true,       // Refetch after mutation
      }
    );
  };
}

Pagination

// components/PostList.tsx — Paginated data
import useSWR from "swr";

function PostList() {
  const [page, setPage] = useState(1);

  const { data, isLoading } = useSWR(`/api/posts?page=${page}&limit=20`);

  return (
    <div>
      {data?.posts.map((post) => <PostCard key={post.id} post={post} />)}
      <button onClick={() => setPage(page - 1)} disabled={page <= 1}>Previous</button>
      <button onClick={() => setPage(page + 1)} disabled={!data?.hasMore}>Next</button>
    </div>
  );
}

Infinite Loading

// components/InfiniteFeed.tsx — Infinite scroll
import useSWRInfinite from "swr/infinite";

function InfiniteFeed() {
  const { data, size, setSize, isLoading } = useSWRInfinite(
    (index) => `/api/feed?page=${index + 1}&limit=20`,
  );

  const posts = data?.flatMap((page) => page.posts) || [];
  const hasMore = data?.[data.length - 1]?.hasMore;

  return (
    <div>
      {posts.map((post) => <PostCard key={post.id} post={post} />)}
      {hasMore && (
        <button onClick={() => setSize(size + 1)} disabled={isLoading}>
          Load More
        </button>
      )}
    </div>
  );
}

Examples

Example 1: Dashboard with auto-refreshing data

User prompt: "Build a dashboard that shows live metrics — refresh every 5 seconds."

The agent will use SWR with refreshInterval: 5000, show cached data instantly on mount, and handle loading/error states.

Example 2: CRUD with optimistic updates

User prompt: "Build a todo app where adding/deleting feels instant."

The agent will use SWR mutations with optimistic data, rollback on error, and automatic revalidation after changes.

Guidelines

  • Key = cache key — same key = same cached data across components
  • null key skips fetching — conditional fetching: useSWR(userId ? /api/... : null)
  • Revalidation on focus — data refreshes when user returns to tab
  • Optimistic updates — show changes immediately, revert on error
  • mutate for cache updates — bound (per-key) or global
  • useSWRInfinite for infinite scroll — accumulates pages
  • Deduplication — multiple components using same key = one request
  • Error retry built-in — automatic with exponential backoff
  • Smaller than TanStack Query — simpler API for simpler needs
  • Works with any fetcher — fetch, axios, GraphQL clients

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/swr-react-data-fetching-caching-ms47vpl1/download' --output '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md.part' && mv '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md.part' '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Environment declared by the author: React 18+. Next.js, Remix, Vite.

Original excerpt: Setup
npm install swr

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md' && test -s '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/swr' && cp -R '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1' '.claude/skills/swr'

Cursor
test -f '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md' && test -s '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/swr' && cp -R '.skill-downloads/swr-react-data-fetching-caching-ms47vpl1' '.cursor/skills/swr'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/swr-react-data-fetching-caching-ms47vpl1'

openskills read 'swr-react-data-fetching-caching-ms47vpl1'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/swr-react-data-fetching-caching-ms47vpl1/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills