mcpField
N
navigation

navigation

github.com/tanstack開発15.3k201 回インストール4 日前に更新
リポジトリ

navigation について

React アプリで型安全なナビゲーションを実装するための TanStack Router スキルです。リンク、プログラムによる遷移、プリロード、遷移ブロック、スクロール復元を扱います。

主な機能

  1. 01型安全なリンクとルートパラメーター
  2. 02アクティブ状態のリンク
  3. 03相対遷移とプログラムによる遷移
  4. 04ルートのプリロード方式
  5. 05遷移ブロックとスクロール復元

活用シーン

  1. 01動的なルートパラメーターを使ったリンクの作成
  2. 02フォーム送信後の画面遷移
  3. 03遷移前のルートのプリロード
  4. 04未保存の変更がある場合の離脱警告

インストール

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

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

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

詳細

ナビゲーション

セットアップ

to と params を指定する、基本的な型安全な Link:

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

function PostLink({ postId }: { postId: string }) {
  return (
    <Link to="/posts/$postId" params={{ postId }}>
      View Post
    </Link>
  )
}

基本パターン

アクティブ状態を持つ Link
import { Link } from '@tanstack/react-router'

function NavLink() {
  return (
    <Link
      to="/posts"
      activeProps={{ className: 'font-bold' }}
      inactiveProps={{ className: 'text-gray-500' }}
      activeOptions={{ exact: true }}
    >
      Posts
    </Link>
  )
}

CSS によるスタイル設定のため、アクティブなリンクには "active" の data-status 属性も設定されます。

activeOptions でマッチングの動作を制御します。

  • exact(デフォルトは false)— true の場合、完全に一致するパスのみを対象にします(子ルートは対象外)
  • includeHash(デフォルトは false)— アクティブ状態のマッチングにハッシュを含めます
  • includeSearch(デフォルトは true)— アクティブ状態のマッチングに検索パラメーターを含めます

子要素では、レンダー関数を使って isActive を受け取れます。

<Link to="/posts">
  {({ isActive }) => <span className={isActive ? 'font-bold' : ''}>Posts</span>}
</Link>
from を使った相対ナビゲーション

from を指定しない場合、ナビゲーションはルート / を基準に解決されます。.. のような相対パスを使うには、from を指定します。

import { createFileRoute, Link } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  component: PostComponent,
})

function PostComponent() {
  return (
    <div>
      {/* Relative to current route */}
      <Link from={Route.fullPath} to="..">
        Back to Posts
      </Link>

      {/* "." reloads the current route */}
      <Link from={Route.fullPath} to=".">
        Reload
      </Link>
    </div>
  )
}
プログラムによるナビゲーションに useNavigate を使う

useNavigate は、副作用を起点としたナビゲーション(フォーム送信後など)に限って使用してください。ユーザーがクリックする要素には、Link を使用してください。

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

function CreatePostForm() {
  const navigate = useNavigate({ from: '/posts' })

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    const response = await fetch('/api/posts', { method: 'POST', body: '...' })
    const { id: postId } = await response.json()

    if (response.ok) {
      navigate({ to: '/posts/$postId', params: { postId } })
    }
  }

  return <form onSubmit={handleSubmit}>{/* ... */}</form>
}

Navigate コンポーネントは、マウント時に直ちにクライアントサイドナビゲーションを実行します。

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

function LegacyRedirect() {
  return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}

router.navigate は、React の外部を含め、ルーターインスタンスを利用できる場所であればどこでも使用できます。

プリロード

方式:intent(ホバー/touchstart)、viewport(交差オブザーバー)、render(マウント時)。

グローバルに設定する場合:

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

const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  defaultPreloadDelay: 50, // ms, default is 50
})

または、リンクごとに設定する場合:

<Link
  to="/posts/$postId"
  params={{ postId }}
  preload="intent"
  preloadDelay={100}
>
  View Post
</Link>

プリロードされたデータは、デフォルトで 30 秒間新鮮な状態に保たれます(defaultPreloadStaleTime: 30_000)。この期間中は再取得されません。TanStack Query などの外部キャッシュを使う場合は、外部ライブラリで鮮度を制御できるように defaultPreloadStaleTime: 0 を設定してください。

ルーターインスタンスを使った手動プリロード:

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

function Component() {
  const router = useRouter()

  useEffect(() => {
    router.preloadRoute({ to: '/posts/$postId', params: { postId: '1' } })
  }, [router])

  return <div />
}
ナビゲーションのブロック

フォームに未保存の変更がある場合は、useBlocker を使ってナビゲーションを防止します。

import { useBlocker } from '@tanstack/react-router'
import { useState } from 'react'

function EditForm() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    shouldBlockFn: () => {
      if (!formIsDirty) return false
      const shouldLeave = confirm('Are you sure you want to leave?')
      return !shouldLeave
    },
  })

  return <form>{/* ... */}</form>
}

withResolver を使ったカスタム UI の場合:

import { useBlocker } from '@tanstack/react-router'
import { useState } from 'react'

function EditForm() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  const { proceed, reset, status } = useBlocker({
    shouldBlockFn: () => formIsDirty,
    withResolver: true,
  })

  return (
    <>
      <form>{/* ... */}</form>
      {status === 'blocked' && (
        <div>
          <p>Are you sure you want to leave?</p>
          <button onClick={proceed}>Yes</button>
          <button onClick={reset}>No</button>
        </div>
      )}
    </>
  )
}

beforeunload は個別に制御します。

useBlocker({
  shouldBlockFn: () => formIsDirty,
  enableBeforeUnload: formIsDirty,
})
再利用可能なナビゲーションオプションに linkOptions を使う

linkOptions を使うと、ナビゲーションオプションオブジェクトの型を早い段階でチェックできます。これにより、エラーはスプレッド構文を使う箇所ではなく、定義時に検出されます。

import {
  linkOptions,
  Link,
  useNavigate,
  redirect,
} from '@tanstack/react-router'

const dashboardLinkOptions = linkOptions({
  to: '/dashboard',
  search: { search: '' },
})

// Use anywhere: Link, navigate, redirect
function Nav() {
  const navigate = useNavigate()

  return (
    <div>
      <Link {...dashboardLinkOptions}>Dashboard</Link>
      <button onClick={() => navigate(dashboardLinkOptions)}>Go</button>
    </div>
  )
}

// Also works in an array for navigation bars
const navOptions = linkOptions([
  { to: '/dashboard', label: 'Summary', activeOptions: { exact: true } },
  { to: '/dashboard/invoices', label: 'Invoices' },
  { to: '/dashboard/users', label: 'Users' },
])

function NavBar() {
  return (
    <nav>
      {navOptions.map((option) => (
        <Link
          {...option}
          key={option.to}
          activeProps={{ className: 'font-bold' }}
        >
          {option.label}
        </Link>
      ))}
    </nav>
  )
}
カスタムコンポーネントに createLink を使う

任意のコンポーネントを TanStack Router の型安全なナビゲーションでラップします。

import * as React from 'react'
import { createLink, LinkComponent } from '@tanstack/react-router'

interface BasicLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {}

const BasicLinkComponent = React.forwardRef<HTMLAnchorElement, BasicLinkProps>(
  (props, ref) => {
    return <a ref={ref} {...props} className="block px-3 py-2 text-blue-700" />
  },
)

const CreatedLinkComponent = createLink(BasicLinkComponent)

export const CustomLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
  return <CreatedLinkComponent preload="intent" {...props} />
}

使用時も完全な型安全性が保たれます。

<CustomLink to="/dashboard/invoices/$invoiceId" params={{ invoiceId: 0 }} />
スクロールの復元

ルーターでグローバルに有効にします。

const router = createRouter({
  routeTree,
  scrollRestoration: true,
})

ネストされたスクロール可能領域の場合:

const router = createRouter({
  routeTree,
  scrollRestoration: true,
  scrollToTopSelectors: ['#main-scrollable-area'],
})

カスタムキャッシュキー:

const router = createRouter({
  routeTree,
  scrollRestoration: true,
  getScrollRestorationKey: (location) => location.pathname,
})

特定のナビゲーションでスクロール位置のリセットを防止します。

<Link to="/posts" resetScroll={false}>
  Posts
</Link>
保留中の UI に MatchRoute を使う
import { Link, MatchRoute } from '@tanstack/react-router'

function Nav() {
  return (
    <Link to="/users">
      Users
      <MatchRoute to="/users" pending>
        <Spinner />
      </MatchRoute>
    </Link>
  )
}

よくある間違い

重大:パラメーターを to 文字列に埋め込む
// WRONG — breaks type safety and param encoding
<Link to={`/posts/${postId}`}>Post</Link>

// CORRECT — use the params option
<Link to="/posts/$postId" params={{ postId }}>Post</Link>

動的セグメントは、ルートパス内で $ を使って宣言します。必ず params を使って渡してください。これは Link、useNavigate、Navigate、router.navigate に適用されます。

中程度:クリック可能な要素に useNavigate を使う
// WRONG — no href, no cmd+click, no preloading, no accessibility
function BadNav() {
  const navigate = useNavigate()
  return <button onClick={() => navigate({ to: '/posts' })}>Posts</button>
}

// CORRECT — real <a> tag with href, accessible, preloadable
function GoodNav() {
  return <Link to="/posts">Posts</Link>
}

useNavigate は、プログラムによる副作用を起点としたナビゲーション(フォーム送信後、非同期アクション後など)に限って使用してください。

高:相対ナビゲーションに from を指定しない
// WRONG — without from, ".." resolves from root
<Link to="..">Back</Link>

// CORRECT — provide from for relative resolution
<Link from={Route.fullPath} to="..">Back</Link>

from がない場合、型安全に補完されるのは絶対パスのみです。.. のような相対パスは、現在のルートではなくルートを基準に解決されます。

高:search にオブジェクトを指定すると既存のパラメーターが失われる
// WRONG — replaces ALL search params with just { page: 2 }
<Link to="." search={{ page: 2 }}>Page 2</Link>

// CORRECT — preserves existing search params, updates page
<Link to="." search={(prev) => ({ ...prev, page: 2 })}>Page 2</Link>

search に通常のオブジェクトを渡すと、すべての検索パラメーターが置き換えられます。以前のパラメーターをスプレッドして選択的に更新するには、関数形式を使ってください。


関連項目

  • 関連項目:router-core/search-params/SKILL.md — Link の search プロパティは検索パラメーターの検証と相互作用します
  • 関連項目:router-core/type-safety/SKILL.md — from による型の絞り込みで、Link の型推論が改善されます

コメント