さとまたプログラミングラボ

ルーティング

App Routerのファイルベースルーティング

基本のルーティング

フォルダ = URL

app/以下のフォルダ構造がURLになる

plaintext
app/
├── page.tsx          →  /
├── about/
│   └── page.tsx      →  /about
├── blog/
│   └── page.tsx      →  /blog
└── contact/
    └── page.tsx      →  /contact
プレビュー
フォルダ作成 → URL自動生成
// 設定ファイル不要!

動的ルーティング

[param] で動的セグメント

パラメータを含むURL

typescript
// app/blog/[slug]/page.tsx
interface Props {
  params: { slug: string }
}

export default function BlogPost({ params }: Props) {
  // /blog/hello-world → params.slug = "hello-world"
  // /blog/nextjs-tutorial → params.slug = "nextjs-tutorial"

  return <h1>記事: {params.slug}</h1>;
}
プレビュー
/blog/[slug]/page.tsx
/blog/hello → slug = "hello"
/blog/world → slug = "world"

複数のパラメータ

ネストした動的セグメント

typescript
// app/shop/[category]/[product]/page.tsx
interface Props {
  params: {
    category: string;
    product: string;
  }
}

export default function ProductPage({ params }: Props) {
  // /shop/electronics/iphone
  // → category = "electronics", product = "iphone"

  return (
    <div>
      <p>カテゴリ: {params.category}</p>
      <p>商品: {params.product}</p>
    </div>
  );
}
プレビュー
/shop/[category]/[product]
// /shop/clothes/shirt
// category="clothes", product="shirt"

[...slug] キャッチオール

複数のセグメントをまとめてキャッチ

typescript
// app/docs/[...slug]/page.tsx
interface Props {
  params: { slug: string[] }
}

export default function DocsPage({ params }: Props) {
  // /docs/getting-started
  // → slug = ["getting-started"]

  // /docs/api/reference/auth
  // → slug = ["api", "reference", "auth"]

  return <p>パス: {params.slug.join("/")}</p>;
}
プレビュー
/docs/[...slug]/page.tsx
// /docs/a/b/c → ["a", "b", "c"]

ルートグループ

(folder) でグループ化

URLに影響せずにフォルダを整理

plaintext
app/
├── (marketing)/
│   ├── about/page.tsx    →  /about
│   └── contact/page.tsx  →  /contact
├── (shop)/
│   ├── products/page.tsx →  /products
│   └── cart/page.tsx     →  /cart
└── (auth)/
    ├── login/page.tsx    →  /login
    └── signup/page.tsx   →  /signup
プレビュー
// (marketing) はURLに含まれない
/about ← (marketing)/about
/login ← (auth)/login

Link コンポーネント

クライアントサイドナビゲーション

typescript
import Link from 'next/link';

export default function Navigation() {
  return (
    <nav>
      <Link href="/">ホーム</Link>
      <Link href="/about">概要</Link>
      <Link href="/blog/hello-world">記事</Link>

      {/* 動的パラメータ */}
      <Link href={`/blog/${slug}`}>動的リンク</Link>

      {/* スクロール位置を維持しない */}
      <Link href="/page" scroll={false}>リンク</Link>
    </nav>
  );
}
プレビュー

useRouter (プログラム的遷移)

ボタンクリック等での遷移

typescript
'use client';

import { useRouter } from 'next/navigation';

export default function LoginButton() {
  const router = useRouter();

  const handleLogin = async () => {
    // ログイン処理...

    // 成功したらダッシュボードへ
    router.push('/dashboard');

    // 履歴を置き換え(戻るボタンで戻れない)
    router.replace('/dashboard');

    // 前のページに戻る
    router.back();

    // ページをリロード
    router.refresh();
  };

  return <button onClick={handleLogin}>ログイン</button>;
}
プレビュー
router.push('/path') // 遷移
router.replace('/path') // 置き換え
router.back() // 戻る

特殊ファイル

page.tsx

ルートのUIを定義

layout.tsx

共有レイアウト

loading.tsx

ローディングUI

error.tsx

エラーUI

not-found.tsx

404ページ

template.tsx

再レンダリングするレイアウト