{"id":24882,"plugin_id":"plugin_connector_690a90ec05c881918afb6a55dc9bbaa1","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:18:44.670Z","digest":"bd3b4e4b9632d3ef38c41c4425cbc4abb0583b8287c9b7151f3b8552e2251026","against":5387,"payload":{"name":"swr","description":"SWR data-fetching expert guidance. Use when building React apps with client-side data fetching, caching, revalidation, mutations, optimistic UI, pagination, or infinite loading using the SWR library.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":90}],"skill_md_contents":"---\nname: swr\ndescription: SWR data-fetching expert guidance. Use when building React apps with client-side data fetching, caching, revalidation, mutations, optimistic UI, pagination, or infinite loading using the SWR library.\nmetadata:\n  priority: 4\n  docs:\n    - \"https://swr.vercel.app/docs\"\n  sitemap: \"https://swr.vercel.app/sitemap.xml\"\n  pathPatterns:\n    - 'lib/fetcher.*'\n    - 'src/lib/fetcher.*'\n    - 'utils/fetcher.*'\n    - 'src/utils/fetcher.*'\n    - 'hooks/use*SWR*'\n    - 'src/hooks/use*SWR*'\n    - 'hooks/useFetch*'\n    - 'src/hooks/useFetch*'\n  importPatterns:\n    - 'swr'\n    - 'swr/*'\n  bashPatterns:\n    - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bswr\\b'\n    - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bswr\\b'\n    - '\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bswr\\b'\n    - '\\byarn\\s+add\\s+[^\\n]*\\bswr\\b'\n  promptSignals:\n    phrases:\n      - \"swr\"\n      - \"useswr\"\n      - \"stale-while-revalidate\"\n    allOf:\n      - [data fetching, client]\n      - [cache, revalidat]\n    anyOf:\n      - \"mutation\"\n      - \"optimistic\"\n      - \"infinite loading\"\n      - \"pagination\"\n    noneOf: []\n    minScore: 6\n---\n\n# SWR — React Hooks for Data Fetching\n\nYou are an expert in SWR v2 (latest: 2.4.1), the React Hooks library for data fetching by Vercel. SWR implements the stale-while-revalidate HTTP cache invalidation strategy — serve from cache first, then revalidate in the background.\n\n## Installation\n\n```bash\nnpm install swr\n```\n\n## Core API\n\n### `useSWR`\n\n```tsx\nimport useSWR from 'swr'\n\nconst fetcher = (url: string) => fetch(url).then(res => res.json())\n\nfunction Profile() {\n  const { data, error, isLoading, mutate } = useSWR('/api/user', fetcher)\n\n  if (isLoading) return <div>Loading...</div>\n  if (error) return <div>Error loading data</div>\n  return <div>Hello, {data.name}</div>\n}\n```\n\n**Key parameters:**\n- `key` — unique string, array, or function identifying the resource (often a URL)\n- `fetcher` — async function that receives the key and returns data\n- `options` — optional config object\n\n**Return values:** `data`, `error`, `isLoading`, `isValidating`, `mutate`\n\n### `useSWRMutation` — Remote Mutations\n\n```tsx\nimport useSWRMutation from 'swr/mutation'\n\nasync function updateUser(url: string, { arg }: { arg: { name: string } }) {\n  return fetch(url, { method: 'POST', body: JSON.stringify(arg) }).then(res => res.json())\n}\n\nfunction Profile() {\n  const { trigger, isMutating } = useSWRMutation('/api/user', updateUser)\n\n  return (\n    <button disabled={isMutating} onClick={() => trigger({ name: 'New Name' })}>\n      Update\n    </button>\n  )\n}\n```\n\n### `useSWRInfinite` — Pagination & Infinite Loading\n\n```tsx\nimport useSWRInfinite from 'swr/infinite'\n\nconst getKey = (pageIndex: number, previousPageData: any[]) => {\n  if (previousPageData && !previousPageData.length) return null\n  return `/api/items?page=${pageIndex}`\n}\n\nfunction Items() {\n  const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher)\n  const items = data ? data.flat() : []\n\n  return (\n    <>\n      {items.map(item => <div key={item.id}>{item.name}</div>)}\n      <button onClick={() => setSize(size + 1)}>Load More</button>\n    </>\n  )\n}\n```\n\n## Global Configuration\n\nWrap your app (or a subtree) with `SWRConfig` to set defaults:\n\n```tsx\nimport { SWRConfig } from 'swr'\n\nfunction App() {\n  return (\n    <SWRConfig value={{\n      fetcher: (url: string) => fetch(url).then(res => res.json()),\n      revalidateOnFocus: false,\n      dedupingInterval: 5000,\n    }}>\n      <Dashboard />\n    </SWRConfig>\n  )\n}\n```\n\n## Revalidation Strategies\n\n| Strategy | Option | Default |\n|---|---|---|\n| On window focus | `revalidateOnFocus` | `true` |\n| On network recovery | `revalidateOnReconnect` | `true` |\n| On mount if stale | `revalidateIfStale` | `true` |\n| Polling | `refreshInterval` | `0` (disabled) |\n| Manual | Call `mutate()` | — |\n\n## Optimistic Updates\n\n```tsx\nconst { trigger } = useSWRMutation('/api/user', updateUser, {\n  optimisticData: (current) => ({ ...current, name: 'New Name' }),\n  rollbackOnError: true,\n  populateCache: true,\n  revalidate: false,\n})\n```\n\n## Conditional Fetching\n\nPass `null` or a falsy key to skip fetching:\n\n```tsx\nconst { data } = useSWR(userId ? `/api/user/${userId}` : null, fetcher)\n```\n\n## Error Retry\n\nSWR retries on error by default with exponential backoff. Customize with:\n\n```tsx\nuseSWR(key, fetcher, {\n  onErrorRetry: (error, key, config, revalidate, { retryCount }) => {\n    if (error.status === 404) return // Don't retry on 404\n    if (retryCount >= 3) return      // Max 3 retries\n    setTimeout(() => revalidate({ retryCount }), 5000)\n  },\n})\n```\n\n## `useSWRSubscription` — Real-Time Data Sources\n\nSubscribe to real-time data (WebSockets, SSE, etc.) with automatic deduplication:\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\nfunction LivePrice({ symbol }: { symbol: string }) {\n  const { data } = useSWRSubscription(\n    `wss://stream.example.com/${symbol}`,\n    (key, { next }) => {\n      const ws = new WebSocket(key)\n      ws.onmessage = (event) => next(null, JSON.parse(event.data))\n      ws.onerror = (event) => next(event)\n      return () => ws.close()\n    }\n  )\n\n  return <span>{data?.price}</span>\n}\n```\n\nThe `subscribe` function receives a `next(error, data)` callback and must return a cleanup function. Multiple components using the same key share a single subscription.\n\n## Key Rules\n\n- **Keys must be unique** — two `useSWR` calls with the same key share cache and deduplicate requests\n- **Fetcher is optional** when set via `SWRConfig`\n- **`mutate(key)`** globally revalidates any hook matching that key\n- **Array keys** like `useSWR(['/api/user', id], fetcher)` — the fetcher receives the full array\n- **Never call hooks conditionally** — use conditional keys (`null`) instead\n"},"changes":[{"path":"/included_files","type":"changed","before":[],"after":[{"relative_path":"agents/openai.yaml","size_in_bytes":90}]}],"summary":"Fields changed: 1. /included_files.","summary_kind":"deterministic","summary_metadata":{}}