TanStack Queryとは?React Queryとの違い・v5の使い方と脆弱性対策
TanStack Query は、React 向けのデータ取得ライブラリ React Query がリブランドされたもので、読み方は「タンスタッククエリ」です。作者 Tanner Linsley 氏の TanStack ブランドに統合され、React だけでなく Vue・Solid・Svelte でも同じ設計思想を使える形へ拡張されました(React 版のパッケージ名は @tanstack/react-query です)。現在のメジャーは v5 系で、2026年6月時点の最新は 5.101.2、v5 は React 18 以上が前提です。名前が変わっただけでなく、cacheTime が gcTime へ、isLoading が isPending へと API も変わっているため、React Query 時代の記事のまま書くと動きません。この記事では定義と React Query との違いから、useQuery/useMutation の実装、v5 の変更点、Next.js での SSR、そして検索需要の多い脆弱性と対策までを最新情報でまとめます。
まとめ:TanStack Queryの要点
- 正体:React Query の後継。サーバー状態(APIから取るデータ)のキャッシュ・再取得・同期を宣言的に扱うライブラリ。読み方はタンスタッククエリ。
- React Queryとの違い:機能の断絶ではなく名称変更とマルチフレームワーク化。React 版は
@tanstack/react-query、旧react-queryパッケージは非推奨。 - v5で変わった点:
cacheTime→gcTime、isLoading→isPending、useQuery はオブジェクト1つの引数に統一、onSuccessなどのコールバック廃止、Hydrate→HydrationBoundary。 - 脆弱性:SSRストリーミング用の実験パッケージに XSS(CVE-2024-24558、5.18.0で修正)。2026年5月の npm 供給網攻撃では query 系パッケージは影響なし。
- 使いどころ:一覧・詳細など「サーバーのデータを表示し、操作後に整合させたい」画面。フォームの入力値など純粋なクライアント状態には使わない。
以下、それぞれを実装コードと合わせて掘り下げます。
TanStack Queryとは:サーバー状態管理という考え方
TanStack Query が扱うのは「サーバー状態」——自分のアプリが所有せず、APIの向こうにあって、いつ古くなるか分からないデータです。useState と useEffect で取得すると、ローディング判定・エラー処理・重複リクエストの抑制・再取得・キャッシュを毎回手書きすることになります。TanStack Query はこの定型処理を引き受け、コンポーネントは「どのキーで何を取るか」だけを宣言します。
React Queryとの違い:名称変更とマルチフレームワーク化
「React Query と TanStack Query は別物か」という検索が多いですが、実体は同じ系譜です。v4 までは react-query という名前でしたが、React 以外への展開に合わせて TanStack ブランドへ統合され、React 版は @tanstack/react-query、Vue 版は @tanstack/vue-query のように framework 別パッケージへ分かれました。コアロジックは @tanstack/query-core に集約され、各フレームワークは薄いアダプタを載せる構成です。つまり「React Query の知識はそのまま TanStack Query に通用する」一方で、後述する v5 の破壊的変更だけは追随が必要になります。
向く画面・向かない画面(サーバー状態とクライアント状態)
向くのは、一覧取得・詳細表示・検索結果・ページネーションなど「サーバーのデータを読み、操作後に最新へ揃えたい」画面です。逆に、入力中のフォーム値・モーダルの開閉・タブの選択状態といった純粋なクライアント状態は TanStack Query の対象外で、useState や状態管理ライブラリで持つべきです。ここを混同してフォーム値までキャッシュに載せると、かえって設計が複雑になります。フォーム自体の実装は React Hook Formとは?使い方・バリデーション・v7の書き方を実例で解説 が参考になります。
バージョンの現在地とv5の主な変更点(v4からの移行)
「tanstack query version」「react query v4」などの検索が示すとおり、どの版を前提にするかで書き方が変わります。現行は v5 系(2026年6月時点で 5.101.2)で、React 18 以上が必須です。v4 以前の記事やコードをそのまま使うと動かない代表的な変更を、移行の観点で押さえます。
useQuery引数のオブジェクト統一とコールバック廃止
v4 では useQuery(key, fn, options) のような複数の書き方(オーバーロード)が許されていましたが、v5 では引数がオブジェクト1つに統一されました。あわせて onSuccess / onError / onSettled のコールバックは useQuery から廃止され、副作用は useEffect かグローバルの QueryCache 側で扱います。
// v4(動かない書き方)
const { isLoading } = useQuery(["todos"], fetchTodos, { cacheTime: 300000 });
// v5
const { isPending } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
gcTime: 300000, // cacheTime から改名
});
cacheTime・loadingの改名(gcTime/pending)
紛らわしかった cacheTime は gcTime(garbage collection time)へ改名されました。これは「データがキャッシュされる時間」ではなく、そのクエリを使うコンポーネントが無くなってから、キャッシュが破棄されるまでの猶予(既定5分)を指します。状態フラグも変わり、初回取得中を表す status は "loading" から "pending" になり、フラグ名も isPending へ。v5 では別途、派生フラグとして isLoading(= isPending && isFetching)が用意され、「キャッシュが無く、かつ通信中」を表します。
keepPreviousDataとHydrateの置き換え(placeholderData/HydrationBoundary)
ページ送り時に前ページの表示を保つ keepPreviousData: true は廃止され、placeholderData に keepPreviousData 関数を渡す形へ変わりました。SSR まわりでは、クライアントでキャッシュを復元するコンポーネントが Hydrate から HydrationBoundary へ改名されています。
import { keepPreviousData } from "@tanstack/react-query";
useQuery({
queryKey: ["projects", page],
queryFn: () => fetchProjects(page),
placeholderData: keepPreviousData, // v4の keepPreviousData: true の置き換え
});
手作業での置換は数が多くなりがちなので、公式が提供する jscodeshift ベースの Codemod で機械的に変換できます(TypeScript では parser に tsx を指定しないと適用されません)。変換後は差分をレビューし、prettier/eslint を通してください。
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
導入とQueryClientの初期設定
インストールとDevtools
本体に加えて、キャッシュの中身を可視化する Devtools を開発用に入れておくと、後述のキャッシュ挙動を目で追えます。
npm install @tanstack/react-query
# 開発時のみ(本番バンドルには含めない運用が基本)
npm install @tanstack/react-query-devtools
QueryClientとQueryClientProviderの配置
QueryClient がキャッシュの本体で、QueryClientProvider でアプリ全体に配ります。既定の挙動は defaultOptions で一括指定できます。staleTime を明示しておくと、初期値0(取得直後から stale 扱い)による過剰なリフェッチを避けられます。
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const queryClient = new QueryClient({
defaultOptions: {
queries: { staleTime: 60 * 1000, retry: 1 },
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<Routes />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
useQueryでのデータ取得(基本とよく使うオプション)
基本構文と状態フラグ
useQuery には一意な queryKey と、Promise を返す queryFn を渡します。返ってくる状態フラグは v5 で意味が整理されており、初回取得中は isPending、失敗時は isError、バックグラウンド再取得中かどうかは isFetching で判定します。
import { useQuery } from "@tanstack/react-query";
function Todos() {
const { data, isPending, isError, error } = useQuery({
queryKey: ["todos"],
queryFn: () => fetch("/api/todos").then((res) => res.json()),
staleTime: 5 * 60 * 1000,
});
if (isPending) return <p>読み込み中...</p>;
if (isError) return <p>エラー: {error.message}</p>;
return <ul>{data.map((t) => <li key={t.id}>{t.title}</li>)}</ul>;
}
API 側の実装と合わせて設計したい場合は、FastAPIとReactとDockerを使用したフルスタックアプリケーションの構築方法 のようにバックエンドのエンドポイント設計まで通して見ると、queryFn に何を書くかが具体化します。
よく検索されるオプション:enabled・select・retry・refetchInterval
個別オプションは単体で検索されるほど需要があります。実務で頻出する4つを整理します。
| オプション | 役割 | 典型的な値 |
|---|---|---|
| enabled | 条件が揃うまでクエリを実行しない(依存クエリ) | !!userId |
| select | 取得データから必要な項目だけ派生させ再レンダリングを抑制 | (d) => d.name |
| retry | 失敗時の再試行回数 | 1(既定は3) |
| refetchInterval | 一定間隔での自動再取得(ポーリング) | 5000 |
特に enabled は、ログイン後にしか叩けないAPIや、前段のクエリ結果を待つ依存関係の制御に効きます。select は一覧の一部だけを使うコンポーネントの無駄な再描画を減らします。
const { data } = useQuery({
queryKey: ["user", userId],
queryFn: () => fetchUser(userId),
enabled: !!userId, // userId が無い間は実行しない
select: (user) => user.name, // 必要な項目だけ取り出す
});
useMutationとキャッシュ更新(作成・更新・削除)
基本と、操作後のinvalidateQueries
データを変更する POST/PUT/DELETE は useMutation で扱います。ポイントは「変更が成功したら、関係するクエリを無効化して再取得させる」こと。これで一覧と詳細の表示が自動で最新に揃います。
import { useMutation, useQueryClient } from "@tanstack/react-query";
function AddTodo() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newTodo) => api.post("/todos", newTodo),
onSuccess: () => {
// 一覧クエリを無効化 → 自動で再取得
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
});
return (
<button onClick={() => mutation.mutate({ title: "買い物" })}>
追加
</button>
);
}
キャッシュ操作は用途で使い分けます。invalidateQueries は「古い印を付けて再取得」、resetQueries は「初期状態へ戻す」、removeQueries は「キャッシュから完全に消す」。多くの場面では invalidateQueries が第一選択です。
楽観的更新(onMutateとロールバック)
通信の完了を待たずに UI を先に更新し、失敗したら元へ戻すのが楽観的更新です。onMutate で更新前の値を退避し、onError で巻き戻します。体感速度を上げたい操作(いいね、チェックの切り替えなど)で効果的です。
useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
await queryClient.cancelQueries({ queryKey: ["todos"] });
const previous = queryClient.getQueryData(["todos"]);
queryClient.setQueryData(["todos"], (old = []) => [...old, newTodo]);
return { previous }; // ロールバック用に退避
},
onError: (err, newTodo, context) => {
queryClient.setQueryData(["todos"], context.previous); // 巻き戻し
},
onSettled: () => queryClient.invalidateQueries({ queryKey: ["todos"] }),
});
キャッシュの仕組みとリフェッチ制御(staleTimeとgcTime)
キャッシュ挙動は staleTime と gcTime の2軸で理解すると迷いません。役割が別物なので分けて覚えます。
| 設定 | 意味 | 既定値 |
|---|---|---|
| staleTime | データを「新鮮」とみなす期間。この間は再取得しない | 0 |
| gcTime | クエリが未使用になってからキャッシュを破棄するまで | 5分 |
既定では staleTime が0なので、マウントやフォーカスのたびに再取得が走ります。更新頻度の低いマスタデータなどは staleTime を長めにして無駄な通信を抑えます。フォーカス時・再接続時の自動再取得は refetchOnWindowFocus などで個別に無効化でき、定期更新は refetchInterval、任意のタイミングでの再取得は useQuery が返す refetch 関数で行います。
Next.jsでのSSR:プリフェッチとHydration
サーバー側でデータを取得してから描画すれば、初期表示のちらつきを抑えられ、クローラにも内容が届きます。App Router では、サーバーコンポーネントで prefetchQuery しておき、dehydrate した状態を HydrationBoundary(v5でHydrateから改名)でクライアントへ引き渡します。
import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query";
export default async function Page() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Todos />
</HydrationBoundary>
);
}
SSR/SSG と検索エンジンの見え方の関係は、React SEO対策とは?SSR・SSG・プリレンダリングの選び方と実装手順 で判断基準を整理しています。React 19 以降の Suspense や use との組み合わせを検討する場合は、React 19のリリースに伴う主要な変更点 と、フロントエンド開発者が理解すべきAsync Reactの技術的背景と全体像 も合わせて確認すると、どこまでを TanStack Query に任せるかの線引きがしやすくなります。
脆弱性とセキュリティ:既知のCVEと安全な使い方
「tanstack query 脆弱性」の検索需要は無視できませんが、多くの入門記事はここに触れません。実際に報告された事案と、実装側でできる対策を切り分けて整理します。
CVE-2024-24558:SSRストリーミングのXSS(5.18.0で修正)
実験的パッケージ @tanstack/react-query-next-experimental に、サーバーサイドで HTML を生成する際の入力処理に起因するクロスサイトスクリプティング(XSS)の脆弱性が報告されました(CVE-2024-24558、CVSS 8.2・High)。修正は 5.18.0 以降で取り込まれているため、Next.js のストリーミング用にこのパッケージを使っている場合は 5.18.0 以上へ更新するのが対策です。コア(@tanstack/react-query 本体)ではなく実験パッケージ側の問題である点も、影響範囲の判断材料になります。
2026年5月のnpm供給網攻撃:query系は影響なし
2026年5月11日、npm レジストリで 42 の @tanstack/* パッケージにわたり計84の悪意あるバージョンが公開される供給網攻撃が発生しました(CVE-2026-45321)。ただし公式のポストモーテムでは、@tanstack/query 系・@tanstack/table 系・@tanstack/form 系・@tanstack/virtual 系・@tanstack/store は「clean(影響なし)」と確認されています。影響を受けたのは主に @tanstack/router / @tanstack/react-start 系でした。とはいえ同じ名前空間である以上、教訓は共通です——バージョンを固定し、package-lock.json を必ずコミットして、身に覚えのないマイナー更新が入り込まないようにすることが最小限の防御になります。
実装側の基本:機密データをキャッシュに残さない
ライブラリの脆弱性とは別に、実装上の注意もあります。TanStack Query のキャッシュはメモリ上に保持されるため、個人情報やトークンを含むレスポンスを長時間キャッシュしたままにすると、ログアウト後も別ユーザーに見えてしまう事故が起きえます。ログアウト時にはキャッシュを明示的に破棄してください。
// ログアウト時に機密データをキャッシュから除去する
queryClient.clear();
よくある質問
React Queryはもう使えないのですか?
使えますが、パッケージが変わりました。旧 react-query は非推奨で、React 版は @tanstack/react-query に移行します。機能は継続しており、知識はそのまま通用します。ただし v5 では cacheTime→gcTime などの破壊的変更があるため、コードの追随は必要です。
タンスタッククエリの読み方・表記は?
「タンスタッククエリ」と読みます。英語表記は TanStack Query、パッケージ名は @tanstack/react-query です。「ten stack」ではありません。
SWRやRTK Queryとの違いは?
いずれもサーバー状態を扱いますが、TanStack Query はキャッシュ制御・楽観的更新・無限スクロール・Devtools まで含む機能量が多く、フレームワーク非依存のコアを持つのが特徴です。軽量さ重視なら SWR、Redux 前提のプロジェクトなら RTK Query という選び分けになります。
cacheTimeが無くなったのはなぜ?
「キャッシュされる時間」と誤解されやすかったためです。実際はクエリが未使用になってから破棄までの猶予を指すので、意味を正確に表す gcTime(ガベージコレクション時間)へ改名されました。
脆弱性が心配です。どうすれば安全に使えますか?
コア本体で広範な脆弱性が続いている状況ではありません。SSR ストリーミング用の実験パッケージを使う場合は 5.18.0 以上にし、バージョン固定とロックファイルのコミットで供給網リスクを抑え、機密データはログアウト時に queryClient.clear() で消す——この3点で実務上の大半はカバーできます。