tanstack-router について
tanstack-routerは、React SPAでTanStack Routerを設定、拡張するためのスキルです。型安全なルーティング、検証済みURL検索状態、ローダー、ルーティング層でのTanStack Query連携、ルート付きコンポーネントのテストを扱います。
主な機能
- 01型安全なルート定義とナビゲーション
- 02ファイルベースとコードベースのルーティング
- 03検証済み検索パラメーター
- 04ルートローダーとプリロード
- 05TanStack Queryとローダーの連携
- 06インメモリのルート付きコンポーネントテスト
活用シーン
- 01React SPAのルーティング設定
- 02型付きルートと検索状態の追加
- 03TanStack Queryによるルートデータの先読み
- 04ルーターが必要なコンポーネントのテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bm629/ agent-skills --skill tanstack-routerインストール後、Claude Code で/tanstack-routerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/auth-and-testing.mdnavigation-and-splitting.mdquery-integration.mdrouting-approaches.mdsearch-and-loaders.mdsources.md詳細
tanstack-router — SKILL.md
バリアント: 標準 · 使用するタイミング: スキルが呼び出され、内容を読み、TanStack Router アプリをセットアップまたは拡張すると、呼び出し元に制御が戻ります。
@tanstack/react-routerv1(1.170.x)、および2026-06時点のTanStack Routerドキュメントに照らして検証済みです。 TanStack Routerのメジャーバージョンはv1です(継続的にリリースされるsemverのマイナーリリース)。以下のAPIは、作成時点で公式のtanstack.com/routerドキュメントと照合しています。オプション名はバージョンによって異なる場合があります。使用中のバージョンで名前が404になる場合は、references/sources.mdに記載されている該当バージョンのドキュメントページを確認してください。
概要
TanStack Routerは、React SPA向けの完全な型安全性を備えたクライアントサイドルーターです。特徴は、ルートツリーが型のソースになることです。TypeScriptの宣言マージでルーターを登録すると、すべてのLink、navigate、useParams、useSearch、useLoaderDataが実際のルートに照らしてチェックされます。そのため、無効なパスや不足しているパラメーターはコンパイルエラーになります。ファーストクラスの検索パラメーター(スキーマで検証されるURLの状態)、ルートローダー(プリロード可能な、レンダリング前に取得されるデータ)、およびコード分割を備えています。このスキルでは、ルーターをセットアップし、型安全にルートを接続し、ローダーからデータ取得へのつなぎ目(TanStack Queryとの連携を含む)と、ルーティングされたコンポーネント用のテストハーネスを扱います。これはルーティングレイヤーです。フルスタックフレームワークのTanStack Startはその上に構築されます(ここでは対象外です。「適用範囲」を参照してください)。
使用するタイミング
- ✅ Vite + React + TypeScriptのSPAでルーティングをセットアップする(または既存のTanStack Routerアプリを拡張する)。
- ✅ ルート、型付きナビゲーション、検証済み検索パラメーター、またはルートローダーを定義する。
- ✅ ルートローダーをTanStack Queryに接続する(ローダーでプリフェッチし、コンポーネントで読み取る)。
- ✅ ルーティングされたコンポーネントをレンダリングするテストを書く(メモリ履歴ハーネスが必要)。
次の場合は有効にしないでください:
- プロジェクトがReact Router(
react-router/ Remix)を使用している場合。これは別のルーターであり、このスキルでは対応しません。 - TanStack StartのSSR / サーバー関数 / ストリーミングデハイドレーションが必要な場合。Startは後段の利用側であり、ここでは扱いません。
- ルーティングとは切り離されたTanStack Queryの仕組み(キャッシュ設定、ミューテーション、ルートに関係しない無限クエリ)が必要な場合。それは
tanstack-queryスキルの役割です。このスキルでは、ルートローダー/コンテキスト経由で利用するクエリのみを扱います。
ワークフロー
ステップ1:バンドラープラグインをインストールして接続する
npm install @tanstack/react-router
npm install -D @tanstack/router-plugin # file-based routing (Vite/Rspack/Webpack)
npm install -D @tanstack/react-router-devtools # optional, dev-only
Viteにプラグインを追加します。現在のエクスポート名は@tanstack/router-plugin/viteの**tanstackRouter(キャメルケース)です。@vitejs/plugin-reactより前に**実行する必要があります。
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
tanstackRouter({ target: 'react', autoCodeSplitting: true }),
react(),
],
})
このプラグインはsrc/routes/を監視し、src/routeTree.gen.tsを生成します。(従来のTanStackRouterViteという名前と@tanstack/router-vite-pluginパッケージもエイリアス経由で引き続き使用できますが、現在の名前はtanstackRouterです。references/sources.mdを参照してください。)
ステップ2:型安全なセットアップ(createRouter + RouterProvider + Register)
これは、アプリ全体のナビゲーションを型安全にするための接続です。Registerの宣言マージは必須です。これがないと、型付きフックがユニオン型にフォールバックし、オートコンプリートとコンパイル時チェックが失われます。
// src/router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
defaultPreload: 'intent', // preload on hover/focus; see Step 6
scrollRestoration: true,
})
// Declaration merging: binds YOUR router's type into the library's hooks.
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
// src/main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from '@tanstack/react-router'
import { router } from './router'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
)
ステップ3:ルートルート
// src/routes/__root.tsx
import { createRootRoute, Outlet, Link } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
export const Route = createRootRoute({
component: () => (
<>
<nav>
<Link to="/">Home</Link>{' '}
<Link to="/posts">Posts</Link>
</nav>
<Outlet />
<TanStackRouterDevtools />
</>
),
})
__root.tsxはすべてのルートをラップし、<Outlet />は一致した子ルートをレンダリングします。型付きルーターコンテキスト(たとえばqueryClientの注入)には、代わりにcreateRootRouteWithContext<T>()を使用してください。references/auth-and-testing.mdおよびreferences/query-integration.mdを参照してください。
ステップ 4:ルートを実装する — ファイルベース(推奨されるデフォルト)
ファイルベースルーティングは TanStack のデフォルトであり、ここでの基本的な方法です。ファイルの場所によって URL が決まります。createFileRoute('/path') は、最初にパス文字列を取り、次にオプションオブジェクトを取ります。パスパラメーター、検証済みの検索条件、ローダー、コンポーネントを備えた完全なルートの例です。
// src/routes/posts.$postId.tsx -> matches /posts/$postId
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'
const postSearchSchema = z.object({
tab: z.enum(['body', 'comments']).catch('body'),
})
export const Route = createFileRoute('/posts/$postId')({
validateSearch: zodValidator(postSearchSchema), // Zod v3 adapter; Zod v4: pass schema directly
loader: async ({ params }) => fetchPost(params.postId),
component: PostComponent,
})
function PostComponent() {
const { postId } = Route.useParams() // { postId: string } — typed
const { tab } = Route.useSearch() // { tab: 'body' | 'comments' } — typed
const post = Route.useLoaderData() // typed to the loader's return
return <article data-tab={tab}>{post.title}</article>
}
Route.useParams()/Route.useSearch()/Route.useLoaderData() は、ファイル内で使用する型付きフックです。ルートオブジェクトがソースとなるため、from 引数は不要です。別のファイルから使用する場合は、useParams({ from: '/posts/$postId' }) または getRouteApi('/posts/$postId') を使います(references/search-and-loaders.md を参照)。
コードベースのルーティング(
getParentRoute+addChildren+ 手動のcreateRouterを使用するcreateRootRoute/createRoute)も完全にサポートされており、references/routing-approaches.mdで一級の扱いを受けています。バンドラープラグインを実行できない場合に使用してください。
ステップ 5:ナビゲーションの基本
import { Link, useNavigate, redirect } from '@tanstack/react-router'
// Declarative — renders a real <a href>; preloads on intent.
<Link to="/posts/$postId" params={{ postId: '123' }} search={{ tab: 'comments' }}
activeProps={{ className: 'active' }}>Post 123</Link>
// Imperative — for side effects (after a form submit, etc.).
const navigate = useNavigate()
navigate({ to: '/posts', search: (prev) => ({ ...prev, tab: 'body' }) })
// Redirect — THROW it from beforeLoad/loader (never navigate in a component for guards).
throw redirect({ to: '/login', search: { redirect: location.href } })
activeOptions、レンダープロップの isActive、ナビゲーションのブロック(useBlocker)、ルートマスキングを含む全機能については、references/navigation-and-splitting.md を参照してください。
ステップ 6:ローダーからデータへの連携(TanStack Query との連携)
ルートローダーはコンポーネントのレンダリング前に実行されるため、マウント時にはデータが揃っており、リンクへのインテントに応じて事前読み込みできます。TanStack Query との推奨される連携方法は、queryOptions ファクトリーを定義し、ローダー内で ensureQueryData を実行してから、コンポーネント内で useSuspenseQuery を使ってデータを読み取ることです。
import { createFileRoute } from '@tanstack/react-router'
import { useSuspenseQuery } from '@tanstack/react-query'
import { postQueryOptions } from '../queries' // a queryOptions factory
export const Route = createFileRoute('/posts/$postId')({
loader: ({ context: { queryClient }, params }) =>
queryClient.ensureQueryData(postQueryOptions(params.postId)),
component: Post,
})
function Post() {
const { postId } = Route.useParams()
const { data: post } = useSuspenseQuery(postQueryOptions(postId)) // cache hit — no refetch
return <article>{post.title}</article>
}
queryClient はルーターコンテキスト(createRootRouteWithContext<{ queryClient: QueryClient }>() + createRouter({ context: { queryClient } }))を介してローダーに渡されます。全連携手順 — await と defer の使い分け、defaultPreloadStaleTime: 0、router.invalidate() を使った変更後の無効化、および公式の tanstack-query スキルへの相互参照 — は references/query-integration.md に記載されています。TanStack Query 自体の仕組み(クエリーキー、変更、一般的なキャッシュ調整)はここでは扱いません。それらについては tanstack-query スキルを参照してください。
ステップ 7:ルーティングされたコンポーネントをテストする
ブラウザーの URL を使わずにテスト対象のルートをレンダリングするには、メモリー内ルーターを構築し、RouterProvider でラップします。このスキルはこのテストハーネスを提供し、コンポーネントテストのスキルはこれを利用します。
import { render, screen } from '@testing-library/react'
import { createMemoryHistory, createRouter, RouterProvider, createRootRoute, createRoute } from '@tanstack/react-router'
function renderRoute(initialPath: string) {
const rootRoute = createRootRoute()
const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', component: HomePage })
const routeTree = rootRoute.addChildren([indexRoute])
const router = createRouter({
routeTree,
history: createMemoryHistory({ initialEntries: [initialPath] }),
})
render(<RouterProvider router={router} />)
}
ルーターは一致するコンポーネントが表示される前にローダーを解決するため、Testing Library の findBy* を使ってレンダリング結果を待ちます。router.navigate(...) でナビゲーションを実行します。型付きテストルーターファクトリー、ファイルベースツリーのバリアント、router.load() の待機を含むテストハーネス全体については、references/auth-and-testing.md を参照してください。
ルール
必ず守るルール(絶対に違反しないでください):
- 必ず
Registerの宣言マージを追加してください。declare module '@tanstack/react-router' { interface Register { router: typeof router } }がないと、型付きフックはすべてのルートのユニオン型に劣化し、コンパイル時のルートチェックができなくなります。 - 認証/リダイレクトのガードは
beforeLoad(またはloader)に置き、コンポーネントには置かないでください。beforeLoadからredirect(...)をスローすると、ルートはレンダリング前に停止します。コンポーネントでガードすると、保護されたコンテンツが先に一瞬表示されてしまいます。 - ローダーが検索パラメーターに依存する場合は、必ず
loaderDepsを使用してください。 依存関係をloaderDepsで宣言しない限り、検索条件が変わってもローダーは再実行されません。省略すると古いデータが返されます。 - すべての検索パラメーターを
validateSearchで検証してください。 検索パラメーターはユーザーが制御できる URL 入力です。検証せずに読み取ると、anyの抜け穴が生じ、実行時のリスクになります。 - 自身のルートファイル以外で型付きフックを使う場合は、
fromを渡すか、Route.*/getRouteApiを使用してください。 共通コンポーネントで引数なしのuseParams()を使うと、そのルート固有の正確な型ではなくユニオン型が返されます。 - クエリーの仕組みはルーティングを通じて扱ってください。 このスキルでは、TanStack Query はローダーとコンテキストの境界部分のみを対象とし、正規の参照先として
tanstack-queryを案内しています。スタンドアロンのクエリーパターンをルーターコードに持ち込まないでください。
推奨事項(必要に応じて変更できます):
- ファイルベースのルーティング(
tanstackRouterプラグインとcreateFileRoute)を優先してください。バンドラープラグインを実行できない場合に限り、コードベースのルーティングを使用してください。 - ユーザーが開始するナビゲーションでは
useNavigateより<Link>を優先してください(実際の<a href>、中クリック、プリロード、アクセシビリティに対応します)。useNavigateは副作用に限って使用してください。 - スムーズなナビゲーションのために
defaultPreload: 'intent'を設定してください。TanStack Query がキャッシュを管理する場合は、defaultPreloadStaleTime: 0を設定してください。 - 特別な理由がない限り、
loaderは初期バンドルに含めてください(コード分割しないでください)。コード分割すると、データを取得する前にサーバーとのラウンドトリップが追加されます。
注意点
TanStackRouterVite/@tanstack/router-vite-pluginは正しそうに見えますが、古い名前です。 現在のエクスポートは@tanstack/router-plugin/viteのtanstackRouterです。古い名前も引き続きエイリアスとして機能するため、コードは「動作」しますが、ドキュメントとのずれが生じます。現在の名前を使用してください。routeTree.gen.tsは生成ファイルです。手動で編集してはいけません。また、createRouterより前に存在している必要があります。./routeTree.genからのインポートに失敗する場合は、プラグインが実行されていない(開発サーバーを起動する/ビルドする)か、Vite の設定でreact()より後に配置されています。- ローダーが気付かれないまま古いデータを返す場合、ほとんどの場合は
loaderDepsの不足が原因です。loaderDeps: ({ search }) => ({ page: search.page })で依存関係を宣言しない限り、?page=2に変更してもローダーは再実行されません。 - 共通コンポーネントで
useParams()の型が「壊れる」ことがあります。fromがないと、すべてのルートのパラメーターのユニオン型になります。from、Route.useParams()(同じファイル内)、またはgetRouteApi(path)(分割したファイル内)を使用してください。 - Zod アダプターのバージョンに関する注意点。 Zod v3 では
@tanstack/zod-adapterのzodValidatorでラップしてください。Zod v4 ではアダプターは不要で、スキーマをそのままvalidateSearchに渡します。古い@tanstack/router-zod-adapterパッケージは非推奨です。 - 遅延データ:生のプロミスを返し、
awaitしないでください。 現在のドキュメントでは、ローダーから 未 await の プロミスを返し、<Await>/useAwaitedで受け取ります。スタンドアロンのdefer()ヘルパーは主に SSR/ストリーミングのシリアライズ(Start)用で、SPA では必須ではありません。references/search-and-loaders.mdを参照してください。 - ローダーによるプリフェッチなしで
useSuspenseQueryを使うと、初回描画時にサスペンドします。 ローダーでensureQueryDataを使うのは、コンポーネントのuseSuspenseQueryでキャッシュヒットさせるためです。ローダーを省略すると、ローダーで解消するはずのウォーターフォールが再発します。
アンチパターン
- 「ローダーではなく、
useEffect内でデータを取得します。」 これではレンダー後にデータを取得するウォーターフォールが再発し、プリロードが機能しなくなります。ローダーを使うことが重要です。 - 「コンポーネント内でリダイレクトしてルートを保護します。」 保護されたコンテンツが一瞬表示され、レンダーも余分に発生します。
beforeLoadで保護してください。 - 「
validateSearchは省略して、useSearch()の値をそのまま読み取ります。」 これは URL に由来する未検証のanyです。URL の形式が不正だと、実行時エラーが発生します。 - 「TanStack Query を統合するので、ここでキャッシュ/ミューテーション API 全体を説明します。」 範囲が逸脱しています。ルートを軸に説明し、詳細は
tanstack-queryを参照してください。 - 「ルーターが対応しているので、TanStack Start の SSR/ストリーミングについても説明します。」 Start は別のプロダクト(後続の利用者)なので、対象外としてください。
出力
このスキルは、動作する TanStack Router のコードを生成します。内容は、vite.config.ts のプラグインエントリー、router.tsx(createRouter と Register)、RouterProvider の接続、ルートモジュール(ファイルベースを主とし、コードベースにも対応)、型付きナビゲーションとフック、検証済み検索パラメーター、TanStack Query に接続したローダー、メモリ履歴を使うテストハーネスです。想定する利用者は、React SPA でルーティングを構築するエンジニアまたはコーディングエージェントと、テストハーネスを再利用する関連コンポーネントテストのワークフローです。
関連項目
tanstack-query— TanStack Query の仕組み(クエリーキー、queryOptions、ミューテーション、キャッシュの調整)に関する正式な情報源です。このスキルではローダーとコンテキストの接点に関するすべてについてこれを参照し、内容を重複させません。vite—@tanstack/router-plugin/viteプラグインを実行する開発サーバー兼バンドラーです。このプラグインがrouteTree.gen.tsを生成します。react-component-testing(関連スキル)— このスキルが提供するテストハーネス(Step 7/references/auth-and-testing.md)を利用します。typescript-typecheck—Registerの拡張と型付きフックは厳格なtsconfigを前提としています。ルートツリーの型チェックはtscによって実行されます。- 境界(名前を挙げるだけで、説明はしない): TanStack Start(SSR/ストリーミング対応のフルスタックフレームワーク — 後続の利用者)、React Router(
react-router/Remix — このスタックでは採用しない代替手段)。
段階的な開示
大量のコンテンツは references/ に格納されており、必要に応じて読み込みます。
references/routing-approaches.md— ルーティング構造を選択または実装する場合に読み込みます。完全なファイル命名規則の表(__root、index、$param、_pathless、(group)、$スプラット、ドット記法、末尾のアンダースコアによるネスト解除)と、コードベースのアプローチ全体(createRoute+getParentRoute+addChildren+ 手動のcreateRouter)、および用途の比較が含まれます。references/search-and-loaders.md— 検索パラメーターとデータ読み込みを詳しく扱う場合に読み込みます。プラグイン可能なバリデーターを使ったvalidateSearch(Zod アダプターの実例、Valibot/カスタム方式についての記載)、検索条件の読み取り/更新、親から子への継承、カスタムシリアライズ、検索ミドルウェア(retainSearchParams/stripSearchParams)、ローダー、loaderDeps、遅延データ(生の Promise +Await)、並列読み込み、コンテキストの読み込みを扱います。references/navigation-and-splitting.md— ナビゲーションとバンドル処理を扱う場合に読み込みます。Linkのオプション、useNavigate、redirect、useBlocker、コード分割(createLazyFileRoute/autoCodeSplitting)、プリロード(defaultPreloadの値、preloadDelay、古さの判定時間)、スクロール復元、ルートマスキングを扱います。references/auth-and-testing.md— 認証パターンまたはテストを扱う場合に読み込みます。createRootRouteWithContext、パスレスの_authenticatedレイアウト、beforeLoadによるリダイレクトガード、notFound()、head/メタデータ、および完全なルーターのテストハーネス(メモリ履歴ファクトリー、ファイルベースツリーのバリエーション、ルーターの準備完了を待つ方法)が含まれます。references/query-integration.md— TanStack Query との接続部分を扱う場合に読み込みます。ensureQueryDataとuseSuspenseQueryの連携、待機と遅延の選択、queryOptionsファクトリー、ミューテーション後のrouter.invalidate()による無効化、staleTime/gcTime/defaultPreloadStaleTimeを扱います。すべてルーティングの観点から説明し、正式なtanstack-queryスキルを相互参照します。references/sources.md— 調査の出典情報(公式のtanstack.com/routerドキュメントページと、forge で確認済みの npm バージョン)です。
このスキルには scripts/ や assets/ は含まれません。
本文の上限
descriptionは 1,024 文字以下です(遵守済み)。- 本文は約 500 行/5,000 トークン以下です。毎ターンのコンテキストに保持し、長い内容は
references/に置いています。 - 各リファレンスファイルは、10k トークン超で警告、25k トークン超でエラーになります。リファレンス全体は、25k トークン超で警告、50k トークン超でエラーになります。