---
title: "useSWRとは？使い方とfetcher・mutate・オプション既定値を解説【SWR 2.5対応】"
url: "https://www.issoh.co.jp/tech/details/4694/"
published: 2024-12-27
updated: 2026-10-25
categories: ["React"]
publisher: "株式会社一創"
---

# useSWRとは？使い方とfetcher・mutate・オプション既定値を解説【SWR 2.5対応】

useSWRは、Vercelが開発するReact用データ取得ライブラリ「SWR」の中心となるフックです。`useSWR(key, fetcher)`の1行で、取得・キャッシュ・重複排除・再検証までを受け持ちます。この記事では2026年10月時点の最新安定版SWR 2.5.1をReact 19・jsdomの検証環境で動かし、fetcherの書き方、カスタムフック化と条件付き取得、オプションの既定値、mutateによる更新、Next.js App Routerでの置き場所までを、挙動を確かめたコードで説明します。

## まとめ：useSWRの基本形と最初に押さえる既定値

- **基本形**：`const { data, error, isLoading } = useSWR(key, fetcher)`。同じキャッシュプロバイダー内では同じキーのデータを共有し、重複排除の対象となるリクエストを1本にまとめます。
- **fetcher**：キーを受け取ってデータを返す関数です。キーを`null`にすると取得を止められ、ID待ちの条件付き取得もこれで書きます。`fetch`はHTTP 404や500でも例外を投げないため、`res.ok`を見て自分で投げないと`error`に入りません。
- **既定値**：フォーカス復帰・再接続時の再検証は有効、重複排除は2,000ミリ秒、エラー時の再試行は回数上限なしで間隔が伸びていきます。
- **更新**：画面の即時反映は`mutate`の`optimisticData`、手動で開始する書き込み処理は`useSWRMutation`に分けます。
- **版**：最新安定版は2.5.1（2026年8月12日）。2.5.0でServer Componentsからキャッシュを渡す`cacheData`（実験的）と、キャッシュを全消去する`unload()`が入りました。

## SWRとuseSWRの関係と2.5系の現在地

### 名前の由来と、SWRとuseSWRが指す範囲の違い

SWRはHTTPキャッシュの拡張ディレクティブ`stale-while-revalidate`（RFC 5861）の頭文字です。まず手元のキャッシュ（古いかもしれない値）を返して画面を出し、裏で最新値を取り直して差し替える、という考え方をReactのフックに持ち込んでいます。

「SWR」はnpmパッケージ`swr`全体、「useSWR」はそのデフォルトエクスポートである読み取り用フックを指します。パッケージには`useSWR`のほかに、無限読み込み用の`swr/infinite`、書き込み用の`swr/mutation`、自動再検証を切った`swr/immutable`、WebSocketなどを購読する`swr/subscription`が同梱されています。HTTPヘッダーとしての`stale-while-revalidate`とライブラリの再検証の対応関係は、[stale-while-revalidateとCache-Control・SWRの再検証を解説した記事](/tech/details/7774/)で掘り下げています。

### SWR 2.5.1の版・対応React・同梱モジュール

| 項目        | 内容（2026年10月25日時点）          |
| --------- | -------------------------- |
| 最新安定版     | 2.5.1（2026年8月12日公開）        |
| 直前の機能追加版  | 2.5.0（2026年8月3日公開）         |
| ベータ版      | 2.6.0-beta.0（2026年9月22日公開） |
| 対応React   | 16.11以降・17・18・19           |
| ライセンス     | MIT                        |
| 週間ダウンロード数 | 約1,940万（2026年9月21〜27日・npm） |

2.5.1は、fetcherを持たないフックに`cacheData`が反映されない不具合と、Suspense利用時に`cacheData`由来の値が再マウントで再検証されない不具合などを直したパッチです。2.6.0-beta.0ではキャッシュタグによる再検証（PR #4336）が試験提供されていますが、ベータのため本番では2.5系を使います（各版の変更点は[SWRのGitHubリリース一覧](https://github.com/vercel/swr/releases)で確認できます）。版を指定して入れるなら`npm install swr@2.5.1`です。

## インストールと最小構成のコード

インストールは`npm install swr`（pnpmなら`pnpm add swr`）だけで、Providerの設置は必須ではありません。次のコードは、読み込み中・失敗・成功の3状態を出し分ける最小構成です。

```
import useSWR from 'swr'

const fetcher = async (url) => {
  const res = await fetch(url)
  if (!res.ok) {
    const error = new Error(`HTTP ${res.status}`)
    error.status = res.status
    throw error
  }
  return res.json()
}

export function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <p>読み込みに失敗しました（{error.status}）</p>
  if (isLoading) return <p>読み込み中…</p>
  return <p>{data.name} さん</p>
}
```

戻り値の`isLoading`と`isValidating`は役割が違います。React 19・jsdom上でモック応答を返すfetcherを使い、初回取得と`mutate()`後の状態を記録すると、次の順に変化しました。

| タイミング         | data      | isLoading | isValidating |
| ------------- | --------- | --------- | ------------ |
| 初回マウント直後      | undefined | true      | true         |
| 初回取得の完了       | 1         | false     | false        |
| mutate()で再検証中 | 1         | false     | true         |
| 再検証の完了        | 2         | false     | false        |

「キャッシュが無い初回だけ」スピナーを出すなら`isLoading`、裏で取り直している最中の小さな表示には`isValidating`を使います。`if (!data) return ...`だけで読み込み中を判定し、先にエラーを判定しない場合は、取得失敗後も読み込み中の表示が残ります。また、取得結果が0やfalseの場合も読み込み中と誤判定します。`isLoading`は2.0.0で追加されました。

もう1つ知っておきたいのが依存の追跡です。SWRは、コンポーネントが実際に読んだ戻り値だけを監視して再描画します。`data`だけを分割代入した部品は、取得が失敗しても再描画されませんでした（描画1回）。`error`も読むと2回になります。エラー表示を出すなら、レンダー中に戻り値の`error`を読み取ります。分割代入でも、戻り値オブジェクトのプロパティ参照でも構いません。

## カスタムフックによる取得処理の共通化

同じデータを複数の画面で使うなら、`useSWR`を部品に直接書かず、データ単位のカスタムフックに包みます。[SWR公式のはじめにガイド](https://swr.vercel.app/ja/docs/getting-started)も「再利用可能にする」の節でこの形を勧めています。

```
// hooks/useUser.js
import useSWR from 'swr'

export function useUser(id) {
  const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
  return { user: data, isLoading, isError: error }
}

// 使う側はURLもfetcherも知らなくてよい
function Avatar({ id }) {
  const { user, isLoading, isError } = useUser(id)
  if (isError) return <p>読み込みに失敗しました</p>
  if (isLoading) return <Spinner />
  return <img src={user.avatar} alt={user.name} />
}
```

この形でアバターと名前の2部品から`useUser(1)`を呼んだところ、fetcherの呼び出しは1回でした。親でデータを取ってpropsで配る必要がなくなり、APIのURLやキーの組み立てを変えるときもフック1か所の修正で済みます。キーの形（文字列か配列か）をフックの中に閉じ込めておくと、後で`mutate`するときにキーの書き間違いも起きにくくなります。

## fetcherの意味と書き方

### fetcherの役割と引数・戻り値

fetcherは直訳すると「取ってくるもの」で、SWRではキーを引数に受け取り、データ（またはデータを返すPromise）を返す関数を指します。SWR自体は通信を行いません。`fetch`、axios、GraphQLクライアントのどれを使うかはfetcher次第で、SWRが管理するのは「いつ呼ぶか」と「結果をどこに置くか」だけです。

毎回渡すのが面倒なら、`SWRConfig`でアプリ共通のfetcherを登録できます。

```
import { SWRConfig } from 'swr'

export function App({ children }) {
  return (
    <SWRConfig value={{ fetcher, revalidateOnFocus: false }}>
      {children}
    </SWRConfig>
  )
}

// 配下では useSWR('/api/user') とキーだけで書ける
```

### fetchのHTTPエラーとres.okによる判定

`fetch`はHTTP 404・500だけではrejectされませんが、通信失敗、不正なURL、リクエストの中断などではrejectされます。`(url) => fetch(url).then((res) => res.json())`という公式サンプルどおりのfetcherで404を返すAPIを叩くと、`error`は`undefined`のまま、`data`にエラー応答の本文（今回は`{"message":"not found"}`）が入りました。前章のコードのように`res.ok`を確認して例外を投げると、`error.status`が404になります。

### 配列キーによるfetcherへの複数値の受け渡し

認証トークンやページ番号など、URL以外の値もキャッシュの区別に使うなら配列キーにします。2.x系ではキー配列がそのまま1つの引数として渡るため、fetcher側で分割代入します。1.x系は配列を展開して複数の引数で渡していたので（swr 1.3.0の配布物で確認）、古い記事の`(url, token) =>`という書き方は2.x では動きません。

```
const { data } = useSWR(
  token ? ['/api/orders', token] : null,
  ([url, token]) => fetchWithToken(url, token)
)
```

## 条件付き取得・依存する取得・初期値の渡し方

### キーをnullにする条件付き取得

キーに`null`を渡すと、fetcherは呼ばれません（`undefined`・`false`・空文字・`0`でも実測0回）。ログイン前やIDが決まる前は取得を止め、値がそろった時点で始める書き方です。IDを50ミリ秒後に決める部品で試すと、IDが`undefined`の間の呼び出しは0回、IDが7に決まった直後に`/api/item/7`が1回だけ呼ばれました。

注意したいのは、キーが`null`の間は`isLoading`も`false`になる点です（取得を始めていないため）。`if (isLoading) return ...`だけで分岐すると、`data`が`undefined`のまま描画に進むので、IDが無い状態の表示も別に用意します。

```
const { data } = useSWR(id ? `/api/item/${id}` : null, fetcher)
```

フックをif文の中に入れて呼び分けるのはReactのフックの規則に反します。取得するかどうかはキーで切り替えます（[SWR公式の条件付きフェッチの解説](https://swr.vercel.app/ja/docs/conditional-fetching)）。

### 前の結果を使う依存する取得

ユーザー情報を取ってから、そのIDでプロジェクト一覧を取る、という順番のある取得は、キーを関数にして書きます。関数の中で例外が出るか`null`などの偽値を返すと、SWRはその取得を保留し、前の取得が終わって再描画されたときにもう一度キーを計算します。

```
const { data: user } = useSWR('/api/user', fetcher)
// user が undefined の間は user.id で例外になり、取得は保留される
const { data: projects } = useSWR(() => `/api/projects?uid=${user.id}`, fetcher)
```

応答に40ミリ秒かかるモックで計ると、`/api/user`の取得開始から43ミリ秒後に`/api/projects?uid=3`の取得が始まりました。最初の応答が返った直後に2本目が動いており、`useEffect`で連鎖させるコードは要りません。ただし2本は直列になるので、互いに依存しない取得まで関数キーでつなぐと、その分だけ表示が遅れます。

### fallbackDataで初期値を渡すときの再検証

サーバーで取った値や仮の値を最初から表示したいときは`fallbackData`を使います。0.x系の`initialData`は、SWR 1.0（2021年8月27日公開）で`fallbackData`に名前が変わりました（[SWR 1.0の公式ブログ](https://swr.vercel.app/ja/blog/swr-v1)）。2.5.1で4通りの設定を比べると次のようになりました。

| 指定したオプション                             | マウント時の取得 | 描画の推移（data／isLoading）      |
| ------------------------------------- | -------- | -------------------------- |
| fallbackDataのみ                        | 1回       | 初期値／true → 取得値／false       |
| fallbackData＋revalidateOnMount: false | 0回       | 初期値／false のまま              |
| fallbackData＋revalidateIfStale: false | 0回       | 初期値／false のまま              |
| initialData（0.x系の名前）                  | 1回       | undefined／true → 取得値／false |

読み取れることは3つあります。1つ目に、`fallbackData`を渡しても既定ではマウント時に取り直します。初期値を入れたら更新されなくなった、という古い記事の失敗談とは逆の挙動です。2つ目に、取り直している間は`data`に初期値が入っているのに`isLoading`が`true`になります。`isLoading`は「キャッシュに値が無く、初回の取得を始めるか」で決まり、`fallbackData`はキャッシュに書き込まれないためです（2.5.1の配布物の`shouldDoInitialRevalidation`で確認）。`if (isLoading) return <Spinner />`を先に書くと、せっかくの初期値が表示されません。3つ目に、2.x で`initialData`と書いても無視され、エラーも警告も出ません。古い記事のコードを写すときは名前を確かめてください。

## useEffectで書いた取得処理との違い

`useEffect`と`useState`で取得処理を書くと、読み込み中・エラー・データの3つの状態と、アンマウント後に結果を捨てる後始末を部品ごとに持つことになります。次の表の左列は、共有キャッシュや重複排除の仕組みを自前で作らず、部品ごとに取得する素朴な実装を前提にしています。useSWRが代わりに引き受けるのは次の部分です。

| 観点           | useEffect＋useState | useSWR    |
| ------------ | ------------------ | --------- |
| 状態の数         | 自前で3つ管理            | 戻り値で受け取る  |
| 同じAPIを2部品で表示 | リクエスト2回            | 1回（実測）    |
| 別画面から戻ったとき   | 再取得まで空             | キャッシュを即表示 |
| タブ復帰時の最新化    | 自前でイベント登録          | 既定で有効     |

一方で、取得と無関係な副作用（購読の開始、DOM操作、ログ送信）は引き続き`useEffect`の担当です。取得が成功したときだけ動かしたい処理は`onSuccess`オプションに書けます。`data`を監視する`useEffect`は、通信せずキャッシュから表示されたマウント時にも走る点が違います。フック全体の役割分担は[Reactフック全18種の一覧と使い分けをまとめた記事](/tech/details/3671/)を参照してください。

## 再検証のタイミングとオプションの既定値

### 主要オプションの既定値一覧

swr 2.5.1の`SWRConfig.defaultValue`の出力と、フック側の既定動作を合わせた一覧です。

| オプション                 | 既定値    | 意味                  |
| --------------------- | ------ | ------------------- |
| revalidateOnFocus     | true   | タブ復帰で再検証            |
| revalidateOnReconnect | true   | 回線復帰で再検証            |
| revalidateIfStale     | true   | キャッシュがあってもマウント時に再検証 |
| refreshInterval       | 0（無効）  | 定期ポーリング間隔           |
| dedupingInterval      | 2000ms | 同一キーの重複排除の窓         |
| focusThrottleInterval | 5000ms | フォーカス再検証の最短間隔       |
| errorRetryInterval    | 5000ms | 再試行の基準間隔            |
| errorRetryCount       | 未設定    | 再試行の上限（未設定＝無制限）     |
| loadingTimeout        | 3000ms | onLoadingSlowの発火まで  |

TanStack Queryの`staleTime`に当たるオプションはSWRにありません（2.5.1の型定義に0件）。「一定時間は再取得しない」を表すには、`dedupingInterval`を延ばすか、次の節の方法で再検証のきっかけを止めます。

### revalidateOnFocusによるタブ復帰時の再取得制御

管理画面を別タブと行き来するたびにAPIが呼ばれるのは、`revalidateOnFocus`が既定で有効なためです。フック単位なら`useSWR(key, fetcher, { revalidateOnFocus: false })`、アプリ全体なら`SWRConfig`に書きます。マスターデータのように一度取れば十分なものは、`import useSWRImmutable from 'swr/immutable'`に置き換えると、フォーカス・再接続時の再検証、キャッシュがあるときのマウント時の再検証、ポーリングがまとめて止まります（キャッシュが無い初回だけ取得します）。

### refreshIntervalによる定期再取得の設定

`refreshInterval: 10000`で10秒ごとに取り直します。既定ではタブが非表示の間とオフライン中は止まり、動かし続けるには`refreshWhenHidden`・`refreshWhenOffline`を`true`にします。値には関数も渡せ、`refreshInterval: (latest) => (latest?.status === 'done' ? 0 : 3000)`のように、処理が終わったらポーリングを止める書き方ができます。

### 低速回線・データセーバー利用時の既定値変更

SWRはブラウザの`navigator.connection`を見て既定値を切り替えます。回線種別が`slow-2g`か`2g`、またはデータセーバーが有効なとき、`errorRetryInterval`は10,000ミリ秒、`loadingTimeout`は5,000ミリ秒になります（swr 2.5.1のソースで確認）。開発機で測った再試行間隔が利用者の端末では倍になり得るため、タイムアウト表示の文言は固定秒数で書かないほうが安全です。

## エラー時の再試行回数と対象ステータスの制御

`errorRetryCount`を指定しない場合、SWRは失敗し続ける限り再試行します。待ち時間は`errorRetryInterval`を基準に、回数に応じて最大256倍まで指数的に伸び、0.5〜1.5倍のゆらぎが掛かります。`errorRetryCount: 2`を指定すると、初回と再試行2回の計3回で止まりました。

「404は取り直しても無駄」「401はログイン画面へ」のように状態コードで分けるなら、`onErrorRetry`で再試行そのものを書き換えます。次の設定では404の再試行が0回になりました（fetcherの呼び出しは初回の1回だけ）。

```
<SWRConfig
  value={{
    onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
      if (error.status === 404 || error.status === 401) return
      if (retryCount >= 5) return
      setTimeout(() => revalidate({ retryCount }), 5000)
    },
  }}
>
  {children}
</SWRConfig>
```

アプリ全体の通知（トースト表示やエラー監視への送信）は、各部品でなく`SWRConfig`の`onError`に1か所だけ書きます。

## mutateとuseSWRMutationによる更新結果の画面反映

### バウンドmutateとグローバルmutateの呼び分け

`useSWR`の戻り値の`mutate`は、そのフックのキーに結び付いた版（バウンドmutate）で、引数なしで呼ぶと再検証だけを行います。別の部品が持つキーを更新したいときは`useSWRConfig()`から取れるグローバルの`mutate(key)`を使います。キーの代わりに関数を渡すと、`mutate((key) => typeof key === 'string' && key.startsWith('/api/todos'))`のように条件に合うキーをまとめて再検証できます。

### optimisticDataによる即時表示と失敗時のロールバック

次のコードは、関数コンポーネント内に置く処理の抜粋です。送信の完了を待たずに一覧へ項目を足し、失敗したら元に戻します。`populateCache`で、サーバーが返した作成済みの1件を現在の一覧に足してキャッシュに書き込むため、追加のGETは発生しませんでした。

```
const { data, mutate } = useSWR('/api/todos', fetcher)

async function addTodo(text) {
  const todo = { id: crypto.randomUUID(), text }
  await mutate(
    async () => {
      const res = await fetch('/api/todos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(todo) })
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return res.json()
    },
    {
      optimisticData: (current) => [...(current ?? []), todo],
      populateCache: (created, current) => [...(current ?? []), created],
      rollbackOnError: true,
      revalidate: false,
    }
  )
}
```

送信を失敗させた場合、表示は`["a"]`→`["a","b"]`→`["a"]`と戻り、`mutate`は例外を投げ直しました。呼び出し側で`try`/`catch`して失敗を利用者に伝える処理は別途必要です。React 19の`useOptimistic`との使い分けは[Transition・Suspense・useOptimisticで非同期UIを組むAsync Reactの解説](/tech/details/11005/)が詳しいです。

### useSWRMutationによる書き込み処理の分離

`useSWRMutation`は、`trigger`を呼ぶまで通信しない書き込み用のフックです。fetcherの第2引数の`arg`に`trigger`へ渡した値が入ります。

```
import useSWRMutation from 'swr/mutation'

async function updateUser(url, { arg }) {
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(arg),
  })
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.json()
}

function RenameButton() {
  const { trigger, isMutating } = useSWRMutation('/api/user', updateUser)
  return (
    <button disabled={isMutating} onClick={async () => {
      try {
        await trigger({ name: 'Ada' })
      } catch {
        window.alert('名前の変更に失敗しました。')
      }
    }}>
      名前を変更
    </button>
  )
}
```

同じキー`/api/user`を`useSWR`で読んでいる部品があると、`trigger`の完了後にそのキーが自動で再検証されます。実測ではPOST 1回とGET 1回が発生し、表示が新しい名前に切り替わりました。

## ページ送りと無限スクロールの実装

### keepPreviousDataによるページ切り替え中の表示維持

変更先のキーにキャッシュやフォールバックデータがなく、`keepPreviousData`も無効なら、`data`はいったん`undefined`になります。1ページ目から2ページ目へ切り替えたとき、既定では「2ページ目・データ無し・isLoading=true」の瞬間が生じ、表が一瞬消えました。`keepPreviousData: true`を付けると、その間も1ページ目のデータが表示され続けます。検索ボックスの入力に合わせて結果を出す画面でも同じ効果があります。

### useSWRInfiniteのgetKeyによるカーソル方式のページ連結

「もっと見る」型の一覧は`swr/infinite`を使います。`getKey`は各ページのキーを返す関数で、`null`を返すと以降のページを取りません。

```
import useSWRInfinite from 'swr/infinite'

const getKey = (pageIndex, previousPageData) => {
  if (previousPageData && previousPageData.nextCursor === null) return null
  if (pageIndex === 0) return '/api/posts?limit=20'
  return `/api/posts?limit=20&cursor=${previousPageData.nextCursor}`
}

function Posts() {
  const { data, size, setSize, isValidating } = useSWRInfinite(getKey, fetcher)
  const posts = data ? data.flatMap((page) => page.items) : []
  const isEnd = data && data[data.length - 1]?.nextCursor === null
  return (
    <div>
      <pre>{JSON.stringify(posts, null, 2)}</pre>
      <button disabled={!data || isEnd || isValidating}
        onClick={() => setSize(size + 1)}>
        もっと見る
      </button>
    </div>
  )
}
```

既定では`initialSize`が1、`revalidateFirstPage`が`true`（ページを足すたびに1ページ目も取り直す）、`parallel`が`false`（前ページの結果を待って順に取る）です。ページ同士に依存が無いオフセット方式なら`parallel: true`で並列に取得できます。

## Next.js App Routerで使うときの置き場所

App Routerでは、フック（`useSWR`・`useSWRInfinite`・`useSWRMutation`）をServer Componentsからインポートできません。フックを使う部品には`'use client'`を付けます。Server Componentsから使えるのは`SWRConfig`・`preload`・`unstable_serialize`です。

2.5.0では、Server Componentsで取得を始めたデータを`SWRConfig`の`cacheData`でクライアントへ渡す方法が[SWR公式のNext.js連携ドキュメント](https://swr.vercel.app/ja/docs/with-nextjs)の推奨になりました（機能自体は実験的扱い）。この章のコードは公式ドキュメントの仕様に基づくもので、Next.jsの実環境では動作を確かめていません。

```
// app/layout.tsx（Server Component）
// getUserFromDatabase・getPostsFromDatabase はアプリ側で用意するデータ取得関数（抜粋例）
import { preload, SWRConfig } from 'swr'

export default function Layout({ children }: { children: React.ReactNode }) {
  const cacheData = {
    ...preload('/api/user', getUserFromDatabase),
    ...preload('/api/posts', getPostsFromDatabase),
  }
  return <SWRConfig value={{ cacheData }}>{children}</SWRConfig>
}
```

`preload`は呼んだ時点で取得を始め、Promiseのまま境界を越えて渡せるため、layout内で`await`しなくても2本が並行して走ります。クライアント側は同じキーで`useSWR('/api/user', fetcher)`と書くだけで、初回はサーバーの結果を使い、ブラウザからの重複リクエストは発生しません。以降の再検証はクライアントのfetcherで行われます。従来の`fallback`もPromiseを受け取れます。`cacheData`の特徴は、サーバー取得結果をクライアントキャッシュに反映し、初回の重複リクエストを抑える点です。Server ComponentsとClient Componentsの境界は[React Server Componentsのサーバーとクライアントの使い分けを実装例で解説した記事](/tech/details/4003/)、Next.js側のキャッシュ層との二重化は[Next.jsのキャッシュの5つの保存先と再検証APIの使い分けを解説した記事](/tech/details/2282/)で整理しています。

もう1つの新機能`unload()`は、ログアウト時に前の利用者のデータを残さないための関数です。`unload({ revalidate: false })`ではキャッシュが空のまま再取得されず、引数なしの`unload()`ではマウント中のフックがすぐに取り直しました。

## SWRを選ばないほうがよい場面

SWRは「読み取りが中心で、少し古い値を一瞬見せてよい画面」に向く道具です。次の条件に当てはまるなら、最初から別のライブラリを選ぶほうが後の手戻りが少なくなります。

- **鮮度を秒単位で管理したい**：`staleTime`でデータの鮮度期間を、`gcTime`で未使用キャッシュの保持期間を指定したい、公式DevToolsで状態を追いたいなら[TanStack Queryのv5の使い方をまとめた記事](/tech/details/10307/)の構成が合います。npmの週間ダウンロード数も、`@tanstack/react-query`（約7,720万）が`swr`（約1,940万）の約4倍です（npm公式APIの2026年9月21〜27日集計）。SWR向けのSWRDevToolsはブラウザ拡張として配布されていますが、SWR公式ドキュメント自身が「Vercel公式プロジェクトではありません」と明記しています。
- **状態管理にRedux Toolkitを使っている**：取得結果もストアで扱うなら、同じ流儀で書ける[Redux ToolkitのRTK Queryの実装と採用判断](/tech/details/16013/)のほうが二重管理を避けられます。
- **GraphQLで正規化キャッシュが必要**：あるクエリで更新した1件を別クエリの一覧にも反映させたいなら、IDで正規化する[Apollo ClientのuseQueryとキャッシュの仕組み](/tech/details/3062/)が向きます。SWRのキャッシュはキー単位のため、この同期は自分で`mutate`を書くことになります。

逆に、Next.jsで閲覧系の画面を多く持ち、取得処理を短く保ちたい案件ならSWRで十分です。覚えるAPIも`useSWR`・`mutate`・`SWRConfig`の3つでほぼ足ります。

## useSWRのよくある質問

### fetcherとは何ですか？

キーを受け取ってデータを返す関数のことです。SWRは自分では通信せず、`fetch`やaxiosでの取得はfetcherに任せます。HTTPエラーを`error`で受けたいときは、fetcherの中で`res.ok`を確認して例外を投げます。

### SWRとuseSWRは何が違いますか？

SWRはnpmパッケージ`swr`全体の名前で、useSWRはそのパッケージが提供する読み取り用のフックです。パッケージには`useSWRInfinite`や`useSWRMutation`なども含まれます。

### revalidateOnFocusをオフにするには？

`useSWR(key, fetcher, { revalidateOnFocus: false })`と指定します。全画面で止めるなら`SWRConfig`の`value`に書きます。再接続時などの再検証もまとめて止めたいときは`swr/immutable`を使います。

### useSWRにstaleTimeのようなオプションはありますか？

ありません。`staleTime`はTanStack Queryのオプションです。SWRで再取得の頻度を抑えるには、`dedupingInterval`を延ばすか、`revalidateOnFocus`・`revalidateIfStale`を`false`にします。

### IDが決まるまでuseSWRの取得を止めるには？

キーを`` id ? `/api/item/${id}` : null ``のように書き、取得しない間は`null`を渡します。`useSWR`自体をif文の中で呼ぶのはフックの規則違反になるため、キーで切り替えます。

### useSWRのキャッシュを消すには？

特定のキーは`mutate(key, undefined, { revalidate: false })`で空にできます。すべてのキャッシュを消すなら、SWR 2.5.0で追加された`unload()`を呼びます。

## 関連記事

- [stale-while-revalidateとは？Cache-ControlとSWRの再検証を解説](/tech/details/7774/)
- [TanStack Queryとは？React Queryとの違い・v5の使い方と脆弱性対策](/tech/details/10307/)
- [Async Reactとは？Transition・Suspense・useOptimisticで非同期UIを組む実装【React 19.3】](/tech/details/11005/)
- [React Server Components（RSC）とは？サーバー・クライアントの違いと使い分けを実装例で解説](/tech/details/4003/)
- [Reactフック（Hooks）とは？全18種の一覧とReact Compiler時代の使い分け【2026年8月版】](/tech/details/3671/)

---

出典: [useSWRとは？使い方とfetcher・mutate・オプション既定値を解説【SWR 2.5対応】](<https://www.issoh.co.jp/tech/details/4694/>)（株式会社一創）
