はじめに
Reactでルーティングといえば、長らくReact Routerが定番でした。でも最近、TanStack Routerという選択肢が急速に注目を集めています。
GitHubで12,500スター以上、645人以上のコントリビューターが開発に参加しているこのライブラリ。正直なところ、最初は「また新しいルーターか」と思っていたんですよ。React Routerで困ってないし、わざわざ乗り換える必要あるのかと。
でも実際に触ってみると、型安全性の徹底具合と検索パラメータの扱いやすさに驚きました。「URLにステートを持たせる」という発想が、これまでのルーターとは一線を画している。30代になって思うのは、こういう「地味だけど確実に開発体験を上げてくれるツール」こそ、積極的に取り入れるべきだということ。
TanStack Routerとは
TanStack Routerは、TanStack Query(旧React Query)を開発したTanner Linsley氏率いるチームが手がけるルーティングライブラリです。「クライアント優先、サーバー対応、完全型安全なルーター」がコンセプト。
現在はReactとSolidに対応しており、最新バージョンはv1系。MITライセンスで公開されています。バンドルサイズは約12KB(gzip)と軽量で、パフォーマンスも申し分ない。
ちなみに、TanStack Startというフルスタックフレームワークも展開されていて、SSRやストリーミング、サーバー関数などの機能を追加できます。Next.jsの代替として注目されている存在ですね。
特徴・メリット
1. エンドツーエンドの型安全性
これ、TanStack Routerの最大の売りです。ルートパス、URLパラメータ、検索パラメータ、ローダーの戻り値まで、すべてが型で守られている。
React Routerだと、パラメータは基本的にstring | undefinedで返ってくるので、自分でパースして型を付ける必要があった。TanStack Routerなら、定義した型がそのまま推論されます。タイプミスによるバグが激減するのは、コスパ的にかなり大きいです。
2. 検索パラメータが一級市民
個人的には、これが一番刺さった機能。TanStack Routerは検索パラメータ(URLの?以降)を「最も強力なステート管理」と位置づけています。
公式いわく「It's like having useState right in the URL!」とのこと。検索パラメータにスキーマ検証をかけられるし、型安全に読み書きできる。ページをリロードしても、URLをシェアしても、状態が保持される。これ、意外と今まで面倒だったことなんですよね。
3. 組み込みのキャッシングとプリフェッチ
データローダーにSWR的なキャッシング機構が組み込まれています。さらに、リンクのホバー時に自動でプリフェッチしてくれるので、ページ遷移が体感的に速くなる。
TanStack QueryやSWRとの連携も設計段階から考慮されているので、既存のデータフェッチライブラリとの共存も問題ない。
4. ファイルベースルーティング対応
Next.jsのようなファイルベースルーティングにも対応しています。routes/ディレクトリにファイルを置くだけでルートが自動生成される。コードベースでの定義も可能なので、プロジェクトに合わせて選べます。
5. DevToolsが充実
TanStack製品らしく、DevToolsの完成度が高い。ルートの状態、検索パラメータ、ローダーの状況などがビジュアルで確認できます。デバッグ時に重宝する。
インストール方法
新規プロジェクトの場合
CLIツールを使うのが一番簡単です。
# ファイルベースルーティング(推奨)
npx create-tsrouter-app@latest my-app --template file-router
# コードベースルーティング
npx create-tsrouter-app@latest my-app
cd my-app
npm run dev
TypeScriptやTailwind CSSの設定も含めて、対話的にセットアップできます。
既存プロジェクトへの導入
# npm
npm install @tanstack/react-router
# yarn
yarn add @tanstack/react-router
# pnpm
pnpm add @tanstack/react-router
必要要件として、React v18以上、TypeScript v5.3以上が推奨されています。
基本的な使い方
ルートの定義(コードベース)
まず、ルートツリーを定義します。
// src/routes.tsx
import { createRootRoute, createRoute, createRouter } from '@tanstack/react-router'
import { Root } from './components/Root'
import { Home } from './pages/Home'
import { About } from './pages/About'
import { User } from './pages/User'
// ルートルートの定義
const rootRoute = createRootRoute({
component: Root,
})
// 各ルートの定義
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: Home,
})
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/about',
component: About,
})
// URLパラメータ付きルート
const userRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/users/$userId',
component: User,
})
// ルートツリーの構築
const routeTree = rootRoute.addChildren([
indexRoute,
aboutRoute,
userRoute,
])
// ルーターの作成
export const router = createRouter({ routeTree })
// 型定義の登録(重要)
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
アプリケーションへの組み込み
// src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from '@tanstack/react-router'
import { router } from './routes'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
)
ルートコンポーネント
// src/components/Root.tsx
import { Outlet, Link } from '@tanstack/react-router'
export function Root() {
return (
<div>
<nav>
<Link to="/">ホーム</Link>
<Link to="/about">About</Link>
<Link to="/users/$userId" params={{ userId: '123' }}>
ユーザー詳細
</Link>
</nav>
<main>
<Outlet />
</main>
</div>
)
}
Linkコンポーネントのtoプロパティが型安全になっているのがポイント。存在しないルートを指定するとTypeScriptがエラーを出してくれます。
URLパラメータの取得
// src/pages/User.tsx
import { useParams } from '@tanstack/react-router'
export function User() {
// userIdの型が自動推論される
const { userId } = useParams({ from: '/users/$userId' })
return <div>ユーザーID: {userId}</div>
}
検索パラメータの活用
TanStack Routerの真価は、検索パラメータの扱いで発揮されます。
スキーマ付き検索パラメータ
import { createRoute } from '@tanstack/react-router'
import { z } from 'zod'
// 検索パラメータのスキーマ定義
const productSearchSchema = z.object({
page: z.number().default(1),
sort: z.enum(['price', 'name', 'date']).default('date'),
category: z.string().optional(),
})
const productsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/products',
validateSearch: productSearchSchema,
component: Products,
})
function Products() {
// 型安全に検索パラメータを取得
const { page, sort, category } = productsRoute.useSearch()
return (
<div>
<p>ページ: {page}</p>
<p>ソート: {sort}</p>
<p>カテゴリ: {category ?? '未選択'}</p>
</div>
)
}
検索パラメータの更新
import { useNavigate } from '@tanstack/react-router'
function ProductFilters() {
const navigate = useNavigate()
const { sort } = productsRoute.useSearch()
const handleSortChange = (newSort: 'price' | 'name' | 'date') => {
navigate({
search: (prev) => ({ ...prev, sort: newSort }),
})
}
return (
<select value={sort} onChange={(e) => handleSortChange(e.target.value as any)}>
<option value="date">日付順</option>
<option value="price">価格順</option>
<option value="name">名前順</option>
</select>
)
}
URLが/products?page=1&sort=priceのように更新され、リロードしても状態が保持される。フィルター機能の実装が格段に楽になります。
データローダーの活用
ルートにデータローダーを定義することで、コンポーネントのレンダリング前にデータを取得できます。
const userRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/users/$userId',
loader: async ({ params }) => {
const response = await fetch(`/api/users/${params.userId}`)
return response.json()
},
component: UserDetail,
})
function UserDetail() {
// ローダーのデータを型安全に取得
const user = userRoute.useLoaderData()
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
}
ローダーはプリフェッチにも対応しているので、リンクをホバーした時点でデータ取得を開始できます。
React Routerとの比較
正直なところ、React Router v6も十分に優秀です。ただ、以下の点でTanStack Routerに優位性があると感じています。
- 型安全性: TanStack Routerの方が徹底している
- 検索パラメータ: スキーマ検証付きで扱えるのはTanStack Routerだけ
- DevTools: TanStack製品らしく充実している
- バンドルサイズ: TanStack Routerの方が軽量
一方、React Routerは歴史が長く情報が豊富、エコシステムも成熟しています。既存プロジェクトで問題なく動いているなら、無理に乗り換える必要はないでしょう。
新規プロジェクトで、TypeScriptを使っていて、型安全性を重視するなら、TanStack Router一択ですね。
まとめ
TanStack Routerを導入すると、以下のメリットが得られます。
- ルーティング周りのコードが型安全になる
- 検索パラメータをステート管理として活用できる
- プリフェッチでページ遷移が高速化する
- DevToolsでデバッグが捗る
30代になって思うのは、「なんとなく動く」コードと「確実に動く」コードの差は、運用フェーズで如実に出るということ。型安全なルーティングは、バグを未然に防ぎ、リファクタリングの安心感を高めてくれます。
React Routerで不満がないなら無理に乗り換える必要はないですが、新規プロジェクトを始めるなら一度検討してみる価値はあると思います。URLを「ただの遷移先」ではなく「ステート管理の一部」として扱う発想は、アプリケーション設計の幅を広げてくれますよ。