mcpField
R
react-router

react-router

github.com/tanstack開発15.3k597 回インストール1 週間前に更新
リポジトリ

react-router について

`@tanstack/react-router` を使う React 開発者向けのスキルです。ファイルベースルーティングを扱い、型推論とクライアント優先のデータ読み込みを重視します。

主な機能

  1. 01Vite でのファイルベースルーティング設定
  2. 02型安全なルーターフックの利用
  3. 03パラメーター、検索条件、ローダーデータの取得
  4. 04ナビゲーションとルーター状態の管理
  5. 05認証コンテキストと遅延データの処理

活用シーン

  1. 01React アプリでファイルベースルートを設定
  2. 02型安全なナビゲーションリンクを構築
  3. 03ルーターローダーデータの取得と表示
  4. 04認証ルートとナビゲーション制御の実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/tanstack/router --skill react-router

インストール後、Claude Code で/react-routerを入力して読み込まれたか確認します。

詳細

React Router (@tanstack/react-router)

このスキルは router-core を基盤としています。基本概念については、まず @tanstack/router-core#router-core をお読みください。

CRITICAL:TanStack Router の型は完全に推論されます。型アサーションや推論された値への型注釈は決して追加しないでください。 CRITICAL:TanStack Router はクライアントファーストです。ローダーはデフォルトでサーバーではなく、クライアント上で実行されます。 CRITICAL:@tanstack/react-router と react-router-dom/react-router を混同しないでください。これらは API が異なる、まったく別のライブラリです。

セットアップ全体:Vite でのファイルベースルーティング

1. 依存関係をインストールする
npm install @tanstack/react-router
npm install -D @tanstack/router-plugin @tanstack/react-router-devtools
2. Vite プラグインを設定する
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    // MUST come before react()
    tanstackRouter({
      target: 'react',
      autoCodeSplitting: true,
    }),
    react(),
  ],
})
3. ルートルートを作成する
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

export const Route = createRootRoute({
  component: RootLayout,
})

function RootLayout() {
  return (
    <>
      <nav>
        <Link to="/" className="[&.active]:font-bold">
          Home
        </Link>
        <Link to="/about" className="[&.active]:font-bold">
          About
        </Link>
      </nav>
      <hr />
      <Outlet />
      <TanStackRouterDevtools />
    </>
  )
}
4. ルートファイルを作成する
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: HomePage,
})

function HomePage() {
  return <h1>Welcome Home</h1>
}
// src/routes/about.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/about')({
  component: AboutPage,
})

function AboutPage() {
  return <h1>About</h1>
}
5. ルーターインスタンスを作成して型を登録する
// src/main.tsx
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({ routeTree })

// REQUIRED — without this, Link/useNavigate/useSearch have no type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

const rootElement = document.getElementById('root')!
if (!rootElement.innerHTML) {
  const root = ReactDOM.createRoot(rootElement)
  root.render(
    <StrictMode>
      <RouterProvider router={router} />
    </StrictMode>,
  )
}

フックのリファレンス

すべてのフックは @tanstack/react-router からインポートします。

useRouter()

ルーターインスタンスに直接アクセスします。

import { useRouter } from '@tanstack/react-router'

function InvalidateButton() {
  const router = useRouter()
  return <button onClick={() => router.invalidate()}>Refresh data</button>
}
useRouterState()

ルーターの状態変化を購読します。状態全体を公開するため、パフォーマンスコストが発生します。マッチまたはロケーションには、useMatches と useLocation を使用してください。

import { useRouterState } from '@tanstack/react-router'

function LoadingIndicator() {
  const isLoading = useRouterState({ select: (s) => s.isLoading })
  return isLoading ? <div>Loading...</div> : null
}
useNavigate()

プログラムによるナビゲーションを行います(ユーザーがクリックする要素には <Link> を使用してください)。

import { useNavigate } from '@tanstack/react-router'

function AfterSubmit() {
  const navigate = useNavigate()

  const handleSubmit = async () => {
    await saveData()
    navigate({ to: '/posts/$postId', params: { postId: '123' } })
  }

  return <button onClick={handleSubmit}>Save</button>
}
useSearch({ from })

検証済みの検索パラメーターを読み取ります。

import { useSearch } from '@tanstack/react-router'

function Pagination() {
  const { page } = useSearch({ from: '/products' })
  return <span>Page {page}</span>
}
useParams({ from })

パスパラメーターを読み取ります。

import { useParams } from '@tanstack/react-router'

function PostHeader() {
  const { postId } = useParams({ from: '/posts/$postId' })
  return <h2>Post {postId}</h2>
}
useLoaderData({ from })

ルートローダーから返されたデータを読み取ります。

import { useLoaderData } from '@tanstack/react-router'

function PostContent() {
  const { post } = useLoaderData({ from: '/posts/$postId' })
  return <article>{post.content}</article>
}
useMatch({ from })

ルートマッチ全体(パラメーター、検索、ローダーデータ、コンテキスト)にアクセスします。

import { useMatch } from '@tanstack/react-router'

function PostDetails() {
  const match = useMatch({ from: '/posts/$postId' })
  return <div>{match.loaderData.post.title}</div>
}
その他のフック

すべて @tanstack/react-router からインポートします。

  • useMatches() — アクティブなルートマッチの配列(パンくずリストに便利です)
  • useRouteContext({ from }) — beforeLoad または親ルートのコンテキストを読み取ります
  • useBlocker({ shouldBlockFn }) — 未保存の変更がある場合にナビゲーションをブロックします
  • useCanGoBack() — boolean を返します。戻るための履歴エントリーがあるかを確認します
  • useLocation() — 現在の解析済みロケーション(pathname、search、hash)
  • useLinkProps({ to, params?, search? }) — カスタムリンク要素用の <a> プロパティを取得します
  • useMatchRoute() — 関数を返します:matchRoute({ to }) => match | false

コンポーネントのリファレンス

RouterProvider

React ツリーの最上位にルーターをマウントします。

<RouterProvider router={router} />
Link

<a> のセマンティクスを備えた、型安全なナビゲーションリンクです。

<Link to="/posts/$postId" params={{ postId: '42' }}>
  View Post
</Link>
Outlet

マッチした子ルートのコンポーネントをレンダリングします。

function Layout() {
  return (
    <div>
      <Sidebar />
      <main>
        <Outlet />
      </main>
    </div>
  )
}
Navigate

宣言的なリダイレクトコンポーネントです。

import { Navigate } from '@tanstack/react-router'

function OldPage() {
  return <Navigate to="/new-page" />
}
Await

未 await のローダープロミスから返された遅延データを、Suspense を使ってレンダリングします。

import { Await } from '@tanstack/react-router'
import { Suspense } from 'react'

function PostWithComments() {
  const { deferredComments } = Route.useLoaderData()
  return (
    <div>
      <h1>Post</h1>
      <Suspense fallback={<div>Loading comments...</div>}>
        <Await promise={deferredComments}>
          {(comments) => (
            <ul>
              {comments.map((c) => (
                <li key={c.id}>{c.text}</li>
              ))}
            </ul>
          )}
        </Await>
      </Suspense>
    </div>
  )
}
CatchBoundary

コンポーネントレベルのエラー処理を行うエラーバウンダリーです(ルートレベルのエラーには、ルートオプションの errorComponent を使用します)。

import { CatchBoundary } from '@tanstack/react-router'
;<CatchBoundary
  getResetKey={() => 'widget'}
  onCatch={(error) => console.error(error)}
  errorComponent={({ error }) => <div>{String(error)}</div>}
>
  <RiskyWidget />
</CatchBoundary>

React 固有のパターン

createLink によるカスタムリンクコンポーネント

型安全性を維持しながら、Link をカスタムコンポーネントでラップします。

import { createLink } from '@tanstack/react-router'
import { forwardRef, type ComponentPropsWithoutRef } from 'react'

const StyledLinkComponent = forwardRef<
  HTMLAnchorElement,
  ComponentPropsWithoutRef<'a'>
>((props, ref) => (
  <a ref={ref} {...props} className={`styled-link ${props.className ?? ''}`} />
))

const StyledLink = createLink(StyledLinkComponent)

// Usage — same type-safe props as Link
function Nav() {
  return (
    <StyledLink to="/posts/$postId" params={{ postId: '42' }}>
      Post
    </StyledLink>
  )
}
ルーターフックを使う再利用可能なコンポーネント

複数のルートでルーターフックを使うコンポーネントを作成するには、from プロパティにルートパスのユニオンを渡します。

function PostIdDisplay({ from }: { from: '/posts/$id' | '/drafts/$id' }) {
  const { id } = useParams({ from })
  return <span>ID: {id}</span>
}

// Usage in different route components
<PostIdDisplay from="/posts/$id" />
<PostIdDisplay from="/drafts/$id" />

このパターンでは、不正確なユニオンを返す strict: false を避けつつ、既知の特定のルート間でコンポーネントを再利用できます。

Auth Provider は RouterProvider をラップする必要がある

ルートで認証コンテキスト(createRootRouteWithContext 経由)を使う場合、認証プロバイダーは RouterProvider の祖先でなければなりません。

// CORRECT — AuthProvider wraps RouterProvider
function App() {
  return (
    <AuthProvider>
      <RouterProvider router={router} />
    </AuthProvider>
  )
}

// WRONG — RouterProvider outside auth provider
function App() {
  return (
    <RouterProvider router={router}>
      <AuthProvider>{/* ... */}</AuthProvider>
    </RouterProvider>
  )
}

または、Wrap ルーターオプションを使って、外部からラップせずにコンテキストを提供します。

const router = createRouter({
  routeTree,
  Wrap: ({ children }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  ),
})

よくある間違い

1. 重大:beforeLoad または loader で React フックを使う

beforeLoad と loader は React コンポーネントではなく、通常の非同期関数です。その中で React フックを呼び出すことはできません。代わりに、ルーターコンテキスト経由で認証状態を渡してください。

// WRONG — useAuth is a React hook, cannot be called here
beforeLoad: () => {
  const auth = useAuth()
  if (!auth.user) throw redirect({ to: '/login' })
}

// CORRECT — read auth from router context
beforeLoad: ({ context }) => {
  if (!context.auth.isAuthenticated) {
    throw redirect({ to: '/login' })
  }
}
2. HIGH:RouterProvider を認証プロバイダー内に誤ってラップする

ルーターは undefined! プレースホルダーを使用して一度だけ作成し、RouterProvider の context プロパティを通じて最新の認証情報を渡します。認証情報の変更時にルーターを再作成しないでください。キャッシュがリセットされ、ツリーが再構築されます。

// CORRECT — create router once, inject live auth via context prop
const router = createRouter({
  routeTree,
  context: { auth: undefined! }, // placeholder, filled by RouterProvider
})

function InnerApp() {
  const auth = useAuth()
  return <RouterProvider router={router} context={{ auth }} />
}

function App() {
  return (
    <AuthProvider>
      <InnerApp />
    </AuthProvider>
  )
}
3. MEDIUM:Await/遅延データの Suspense 境界がない

Await には <Suspense> の祖先要素が必要です。ない場合、遅延プロミスのフォールバック UI が表示されず、エラーがスローされます。

// WRONG — no Suspense boundary
<Await promise={deferredData}>{(data) => <div>{data}</div>}</Await>

// CORRECT — wrap in Suspense
<Suspense fallback={<div>Loading...</div>}>
  <Await promise={deferredData}>{(data) => <div>{data}</div>}</Await>
</Suspense>

相互参照

  • @tanstack/router-core#router-core — ドメイン固有のパターンに関するすべてのサブスキル(検索パラメーター、データ読み込み、ナビゲーション、認証、SSR など)

コメント