Skip to main content

第04章:ルーティングでページ分割する 🧭✨(React Routerで「URL=画面」を作る)

この章では、管理画面アプリを 「/dashboard」「/users」「/settings」 に分割して、URL直打ち&リロードでも崩れない “ページ遷移の土台” を作ります🚀 (2026-02-16時点の最新ルート設計として、React Router v7系(latest 7.13.0) 前提で進めます)(reactrouter.com)


1) まずは超ざっくり理解 🧠💡

SPAの「ルーティング」って何?🤔

ReactのSPAは基本 1枚のHTML(index.html) で動きます。 でもユーザーから見ると「ページがある」ようにしたいので…

  • URL(/users など)を見て
  • 表示するコンポーネント(画面)を切り替える

これがルーティングです🧭✨ つまり URL=アプリの状態 です(どの画面を見てるか、どのユーザーを開いてるか、など)📌

firebase_frontend_foundation_ts_study_004_01_routing_concept


2) 今日の推奨:React Router v7 の入れ方 🧰✨

React Router v7 のドキュメントでは、インストールは react-router を入れる形が基本になっています。(reactrouter.com)

PowerShell(またはターミナル)で👇

npm i react-router

以前の情報で react-router-dom を見かけることもあるけど、v7の公式ガイドは react-router を中心に書かれています🧭(reactrouter.com)


3) ハンズオン:3ページ(+404)を作る 🛠️🎉

ゴール 🎯

  • /dashboard /users /settings に行ける
  • URL直打ちでも表示される
  • 変なURLは 404 ページが出る

Step A:ページ(画面)コンポーネントを作る 📄✨

src/pages/ を作って、まずは“文字だけページ”を作ります🙂

src/pages/DashboardPage.tsx

export function DashboardPage() {
return (
<div className="space-y-2">
<h1 className="text-2xl font-bold">Dashboard 📊</h1>
<p className="text-slate-700">ここが管理画面のトップだよ!</p>
</div>
);
}

src/pages/UsersPage.tsx

import { Link } from "react-router";

const demoUsers = [
{ id: "u001", name: "Aさん" },
{ id: "u002", name: "Bさん" },
{ id: "u003", name: "Cさん" },
] as const;

export function UsersPage() {
return (
<div className="space-y-3">
<h1 className="text-2xl font-bold">Users 👥</h1>

<ul className="list-disc pl-5 text-slate-800">
{demoUsers.map((u) => (
<li key={u.id}>
<Link className="underline" to={`/users/${u.id}`}>
{u.name}(詳細へ)
</Link>
</li>
))}
</ul>

<p className="text-sm text-slate-600">
※ 今はダミー。後でFirestoreの一覧に置き換えるよ🗃️
</p>
</div>
);
}

src/pages/SettingsPage.tsx

export function SettingsPage() {
return (
<div className="space-y-2">
<h1 className="text-2xl font-bold">Settings ⚙️</h1>
<p className="text-slate-700">設定画面(後でログイン必須にする)</p>
</div>
);
}

src/pages/NotFoundPage.tsx

import { Link } from "react-router";

export function NotFoundPage() {
return (
<div className="space-y-3">
<h1 className="text-2xl font-bold">404 😵</h1>
<p className="text-slate-700">そのページは見つからなかったよ…</p>
<Link className="underline" to="/dashboard">
Dashboardへ戻る
</Link>
</div>
);
}

Step B:レイアウト(共通枠)を作る 🧱✨

ここでは“ミニ版の共通枠”だけ作ります(本格レイアウトは次章でガッツリ!)💪 React Routerは 親(枠)+子(ページ)Outlet で合成できます。(reactrouter.com)

firebase_frontend_foundation_ts_study_004_02_layout_outlet

src/layouts/AppShell.tsx

import { NavLink, Outlet } from "react-router";

const navItems = [
{ to: "/dashboard", label: "Dashboard" },
{ to: "/users", label: "Users" },
{ to: "/settings", label: "Settings" },
] as const;

export function AppShell() {
return (
<div className="min-h-screen bg-slate-50 text-slate-900">
<header className="border-b bg-white">
<div className="mx-auto flex max-w-5xl items-center gap-4 p-4">
<div className="text-lg font-semibold">My Admin 🧩</div>

<nav className="flex gap-2">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
[
"rounded px-3 py-1 text-sm",
isActive
? "bg-slate-900 text-white"
: "text-slate-700 hover:bg-slate-100",
].join(" ")
}
>
{item.label}
</NavLink>
))}
</nav>
</div>
</header>

<main className="mx-auto max-w-5xl p-4">
<Outlet />
</main>
</div>
);
}

Step C:ルーター定義(URL ↔ 画面)を作る 🧭

React Router v7(Data Mode)では createBrowserRouter でルートをオブジェクトとして定義できます。(reactrouter.com) さらに、pathなしの親ルートを使うと「URLを増やさずに枠だけ作る(Layout Route)」ができます。(reactrouter.com)

firebase_frontend_foundation_ts_study_004_03_router_tree

src/router.tsx

import { createBrowserRouter } from "react-router";

import { AppShell } from "./layouts/AppShell";
import { DashboardPage } from "./pages/DashboardPage";
import { UsersPage } from "./pages/UsersPage";
import { SettingsPage } from "./pages/SettingsPage";
import { NotFoundPage } from "./pages/NotFoundPage";
import { UserDetailPage } from "./pages/UserDetailPage";

export const router = createBrowserRouter([
{
Component: AppShell,
children: [
{ index: true, Component: DashboardPage }, // "/" はDashboard
{ path: "dashboard", Component: DashboardPage },// "/dashboard" もDashboard
{ path: "users", Component: UsersPage }, // "/users"
{ path: "users/:userId", Component: UserDetailPage }, // "/users/u001" みたいなやつ
{ path: "settings", Component: SettingsPage }, // "/settings"
{ path: "*", Component: NotFoundPage }, // それ以外は404
],
},
]);

:userId のような 動的セグメントは、URLから値を取り出す王道パターンです🧩(後でFirestoreの「詳細ページ」と相性バツグン!)(reactrouter.com)

firebase_frontend_foundation_ts_study_004_04_dynamic_route


Step D:User詳細ページ(動的ルートの練習)🧑‍💻✨

src/pages/UserDetailPage.tsx

import { Link, useParams } from "react-router";

export function UserDetailPage() {
const { userId } = useParams();

return (
<div className="space-y-3">
<h1 className="text-2xl font-bold">User Detail 🪪</h1>
<p className="text-slate-700">
userId: <span className="font-mono">{userId}</span>
</p>

<Link className="underline" to="/users">
Usersへ戻る
</Link>
</div>
);
}

Step E:main.tsx を RouterProvider に差し替える 🔁

公式のData Modeでは、RouterProvider を使ってルーターをアプリに渡します。(reactrouter.com)

src/main.tsx(例)

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider } from "react-router/dom";
import { router } from "./router";
import "./index.css";

createRoot(document.getElementById("root")!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
);

4) 動作チェック(URL直打ちミッション)✅🧪

開発サーバー起動👇

npm run dev

ブラウザでこれを順に試してね👇😆

  • http://localhost:5173/dashboard
  • http://localhost:5173/users
  • http://localhost:5173/users/u001
  • http://localhost:5173/settings

そして最大のチェック💥 それぞれのページで F5(リロード)しても表示が崩れない?


5) つまづきポイント集 🧯😵‍💫

❌ ルートは切り替わるのに「中身が出ない」

👉 たいてい <Outlet /> を置き忘れです。 親(AppShell)が枠で、子(各ページ)が中身。子はOutletに出ます🧩(reactrouter.com)

<a href="/users"> で遷移すると画面が“全部リロード”される

👉 React Routerでは Link / NavLink を使うのが基本!🚀 (SPA体験が一気に“それっぽく”なるよ)

❌ デプロイ後に「直URL」や「リロード」で404になる(超ある)😭

これは静的ホスティングあるあるです。 解決は「どのURLで来ても index.html を返す」設定にします。

firebase_frontend_foundation_ts_study_004_05_hosting_rewrite

Firebase Hostingなら、firebase.jsonrewrite を入れるのが王道です👇(Firebase)

{
"hosting": {
"public": "dist",
"rewrites": [{ "source": "**", "destination": "/index.html" }]
}
}

これで /users 直アクセスでも、まずindex.htmlが返ってきて、そこからReact Routerが正しい画面に切り替えます🧠✨


6) AI活用(開発を爆速にするコツ)🤖⚡

🧩 ルーティングはAIに“設計表”を作らせると早い

例:Gemini(エージェント/CLI)にこう頼むと便利👇

  • 「/dashboard /users /settings の3ページ構成の管理画面で、createBrowserRouter 使って最小実装して」
  • 「Layout Route + Outlet で共通ヘッダー付きにして」
  • 「/users/:userId の詳細も追加して」

React Routerの基本構造(Layout Route / Outlet / 動的セグメント)は公式ガイドに載ってるので、AIに参照させるとブレにくいです📚(reactrouter.com)

🛸 Firebase側もAI連携が進んでる(Gemini CLI / MCP など)

Firebaseには Gemini CLI拡張MCPサーバー の公式ドキュメントが用意されています。(Firebase) さらにプロンプト集(Prompt catalog)も公式で整備されていて、更新も新しめです(Last updated 2026-02-05)。(Firebase)

この章では“ルーティング作業の補助”として使うだけでOK🙆‍♂️ 本格的にFirebase AI(AI Logic)を繋ぐのは後半章でやるよ🤖✨


7) Firebase AI Logic と「安全にAIを呼ぶ」考え方(予告)🔐🤖

今はまだ繋がないけど、後でAIボタンを付けるときに超大事な話👇 **クライアントからAIを呼ぶ時は「キーを守る」「悪用を防ぐ」**が必須です🧯

Firebase AI Logic は、クライアントSDKからAIを呼ぶときの仕組み(プロキシ的な立ち位置)を説明しています。(Firebase) ルーティングができてると、たとえば /users/:userId の詳細画面に 「AIでプロフィール文を整形」ボタンみたいなのを自然に置けます😆✨


8) ミニ課題 🎯✨

ミニ課題A(必須)✅

  • /dashboard /users /settingsURL直打ち
  • それぞれ リロード
  • 404にならず表示できることを確認!

ミニ課題B(おかわり)🍚

  • /users/:userId を追加したので Users から u001 などへ遷移して、URLの値が画面に出るのを確認👀✨

9) チェックリスト ✅📌

  • createBrowserRouter で routes を定義できた
  • AppShell<Outlet /> があり、ページが表示される
  • Link / NavLink で遷移して、画面がリロードされない
  • /users/u001 みたいな動的URLで値が取れる
  • (将来)Firebase Hosting の rewrite を入れる意味がわかった

次の第5章では、ここで作った AppShell左サイドバー+上部バーの“管理画面っぽい骨格” に進化させます🧱📊✨