React RouterはReactエコシステムで最も広く使われているルーティングライブラリです。Reactを触ったことがあれば一度は使ったことがあるはずですが、v7のリリースでいくつかの重要な変更が入ったこともあり、基礎から改めて整理する価値があります。
この記事では基本的な内容に絞って解説します。セットアップ、ナビゲーション、動的ルート、ネストされたルート、そしてよく使うフックが対象です。v5/v6からの移行を検討している方にも適した内容です。
動作環境: React Router v7、TypeScript、React 18+
v7からはすべてのimportがreact-routerからになりました。react-router-domはreact-routerに統合されています。
インストール
npm install react-router
v6からの移行であれば、import元を react-router-dom から react-router に一括置換するだけで対応できます。
基本セットアップ
Step 1: アプリを BrowserRouter でラップする
// main.tsx
import { BrowserRouter } from 'react-router'
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<App />
</BrowserRouter>
)
BrowserRouter はHTML5 History APIを使ってURLとUIを同期させます。ページリロードなしにURLを変更できるのはこの仕組みのおかげです。BrowserRouter はルート(通常は main.tsx または index.tsx)に1つだけ配置してください。
Step 2: App.tsx でルートを定義する
// App.tsx
import { Routes, Route } from 'react-router'
import Home from './pages/Home'
import About from './pages/About'
import Contact from './pages/Contact'
import NotFound from './pages/NotFound'
export default function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
)
}
path="*" はワイルドカードで、上記いずれのルートにもマッチしないURLをすべて受け取ります。404ページの実装に使います。
ナビゲーション: Link と NavLink
import { Link, NavLink } from 'react-router'
export function Navbar() {
return (
<nav>
<Link to="/">ホーム</Link>
<Link to="/about">About</Link>
<NavLink
to="/contact"
className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}
>
Contact
</NavLink>
</nav>
)
}
| コンポーネント | 使いどころ |
|---|---|
Link |
シンプルなページ遷移 |
NavLink |
現在のルートに応じてメニュー項目をハイライトしたい場合 |
注意:
<a href>ではなく必ず<Link>を使ってください。<a>タグはページ全体をリロードしてしまい、SPAのステートが失われます。
動的ルート
/users/123 や /posts/react-router-guide のように、URLに動的な値を含む場合に使います。
// App.tsx
<Route path="/users/:userId" element={<UserDetail />} />
<Route path="/posts/:slug" element={<PostDetail />} />
// pages/UserDetail.tsx
import { useParams } from 'react-router'
export default function UserDetail() {
const { userId } = useParams<{ userId: string }>()
return <h1>ユーザー詳細 #{userId}</h1>
}
useParams は path で定義したすべてのパラメータをオブジェクトとして返します。TypeScriptではジェネリック型を渡すことで型安全に扱えます。
// 複数パラメータの例: /posts/:postId/comments/:commentId
const { postId, commentId } = useParams<{
postId: string
commentId: string
}>()
ネストされたルートと共有レイアウト
React Routerの中でも特に重要なコンセプトです。各ページにレイアウトを繰り返し書く代わりに、共通レイアウトコンポーネントを1つ定義し、React Routerが適切な位置にページコンテンツを差し込みます。
// App.tsx
import DashboardLayout from './layouts/DashboardLayout'
import DashboardHome from './pages/DashboardHome'
import Settings from './pages/Settings'
import Profile from './pages/Profile'
export default function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} /> {/* /dashboard */}
<Route path="settings" element={<Settings />} /> {/* /dashboard/settings */}
<Route path="profile" element={<Profile />} /> {/* /dashboard/profile */}
</Route>
</Routes>
)
}
// layouts/DashboardLayout.tsx
import { Outlet, NavLink } from 'react-router'
export default function DashboardLayout() {
return (
<div style={{ display: 'flex' }}>
<aside>
<NavLink to="/dashboard">概要</NavLink>
<NavLink to="/dashboard/settings">設定</NavLink>
<NavLink to="/dashboard/profile">プロフィール</NavLink>
</aside>
<main>
<Outlet /> {/* 子ルートのコンポーネントがここにレンダリングされる */}
</main>
</div>
)
}
2つの重要なコンセプト:
<Outlet /> — React Routerが現在の子ルートのコンポーネントをレンダリングする場所です。Outlet を置き忘れると、ルート設定が正しくても子ルートが表示されません。
<Route index> — 親ルートのpathと同じURLに対応します。/dashboard にアクセスしたとき、DashboardLayout の Outlet の中に DashboardHome がレンダリングされます。
useNavigate — コードからのページ遷移
フォームの送信成功後、ログイン完了後、レコード削除後など、イベントに応じてプログラムからナビゲートしたいときに使います。
import { useNavigate } from 'react-router'
export function LoginForm() {
const navigate = useNavigate()
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
try {
await login(credentials)
// replace: true — ログインページをhistoryスタックから削除する
// ユーザーがBackボタンでログインページに戻れなくなる
navigate('/dashboard', { replace: true })
} catch (error) {
console.error('ログインに失敗しました')
}
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">ログイン</button>
</form>
)
}
よく使うパターンをまとめると:
navigate('/dashboard') // 通常の遷移
navigate('/dashboard', { replace: true }) // 現在のhistoryエントリを置き換える
navigate(-1) // 1つ前のページに戻る(ブラウザのBackと同等)
navigate(1) // 1つ先のページに進む
useSearchParams — クエリ文字列の管理
クエリ文字列とはURLの ? 以降の部分のことです(例: /products?page=2&category=shoes)。新しいルートを追加せずにUIの状態をURLに保持する手段として有効で、URLをそのまま共有すれば同じページ・同じフィルター状態を再現できます。
import { useSearchParams } from 'react-router'
export function ProductList() {
const [searchParams, setSearchParams] = useSearchParams()
const page = Number(searchParams.get('page') ?? '1')
const category = searchParams.get('category') ?? ''
const handlePageChange = (newPage: number) => {
setSearchParams({ page: String(newPage), category })
}
const handleCategoryChange = (newCategory: string) => {
// カテゴリ変更時はpage 1にリセット
setSearchParams({ page: '1', category: newCategory })
}
return (
<div>
<select
value={category}
onChange={(e) => handleCategoryChange(e.target.value)}
>
<option value="">すべて</option>
<option value="shoes">シューズ</option>
<option value="shirts">シャツ</option>
</select>
{/* 商品一覧 */}
<button onClick={() => handlePageChange(page + 1)}>
次のページ →
</button>
</div>
)
}
Tip:
setSearchParamsはuseStateと同様に動作し、クエリ文字列を全体ごと置き換えます。特定のパラメータだけ更新して他を保持したい場合は、関数形式で渡してください:setSearchParams(prev => { prev.set('page', String(newPage)) return prev })
まとめ
| やりたいこと | 使うもの |
|---|---|
| ルーターのセットアップ |
<BrowserRouter> でアプリをラップ |
| ルートの定義 |
<Routes> + <Route>
|
| ページリンク | <Link to="..."> |
| アクティブ状態付きリンク |
<NavLink> + className={({ isActive }) => ...}
|
| 動的パラメータを含むルート |
path="/users/:id" + useParams<{ id: string }>()
|
| 共通レイアウト | ネストされたルート + <Outlet />
|
| グループのデフォルトルート | <Route index> |
| コードからの遷移 | useNavigate |
| クエリ文字列の読み書き | useSearchParams |
| 404ページ | <Route path="*"> |
ここで紹介した内容は基礎となる部分です。より複雑なパターンに進む前に、これらをしっかり理解しておくことをおすすめします。第2回では、loader / action を使ったData mode、Protected Routes、Lazy Loading、そして大規模プロジェクトでのルート設計について解説します。
