3

ルーティングとレイアウト(Layouts & Templates)

Next.js App Routerでは、ファイルシステムをベースとした直感的なルーティングと、状態を維持できる入れ子(ネスト)状のレイアウトシステムが採用されています。

第3章では、フォルダー構成がどのようにページURLに対応するのか、そして画面全体を効率的に構成するためのレイアウトやテンプレート、ルートグループの仕組みを図解で解説します。


1. ファイルシステムベースのルーティング

App Routerでは、app ディレクトリ内のフォルダー構造がそのままURLパスになります。 ページとして公開するルートには、必ずフォルダー内に page.tsx(または .jsx)を配置する必要があります。

Rendering diagram...
graph TD subgraph FolderStructure ["app ディレクトリの構造"] app(["app/"]) app --> layout["layout.tsx <br> 共通レイアウト"] app --> page["page.tsx <br> /"] app --> about["about/"] about --> page_about["page.tsx <br> /about"] app --> blog["blog/"] blog --> page_blog["page.tsx <br> /blog"] blog --> slug["slug/"] slug --> page_detail["page.tsx <br> /blog/:slug"] end style app fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a style page_about fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#14532d style page_blog fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#14532d style page_detail fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#14532d
  • 動的ルーティング ([slug]): フォルダー名をブラケットで囲む(例: [id][slug])ことで、動的なURLパラメータ(例: /blog/nextjs-routing)を受け取るページを作成できます。

2. レイアウト(Layout)とテンプレート(Template)

画面を構築する際、ヘッダーやサイドナビゲーションなどの「共通UI」を定義するために layout.tsxtemplate.tsx を使用します。

レイアウト(Layout)の特徴

  • 状態の維持 (State Preservation): ユーザーが子ルート間で遷移しても、レイアウトは再レンダリングされず、レイアウト内の状態(入力フォームの値やスクロール位置など)が維持されます。
  • DOMの再作成なし: 遷移時にレイアウト部分のHTML要素は破棄されず、効率的に再利用されます。

テンプレート(Template)の特徴

  • 毎回リセットされる: レイアウトと似ていますが、ルート遷移するたびにテンプレート内のインスタンスが新しく作成され、状態はリセットされます。
  • ユースケース: ページ遷移ごとのフェードインアニメーションや、アクセス解析のページビュー計測(useEffect の再実行)を行いたい場合に適しています。
Rendering diagram...
graph TD subgraph LayoutFlow [Layoutの入れ子構造] GL[Root Layout <br> ヘッダー・フッター] GL --> SL[Blog Layout <br> カテゴリリスト] SL --> P1[Blog List Page <br> /blog] SL --> P2[Blog Detail Page <br> /blog/:slug] end style GL fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a style SL fill:#faf5ff,stroke:#a855f7,stroke-width:2px,color:#581c87 style P1 fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#14532d style P2 fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#14532d

3. ルートグループ(Route Groups)

「URLのパス名には影響を与えたくないが、特定のページ群だけに共通のレイアウトを適用したい」または「ファイルを論理的なグループで整理したい」という場合には、ルートグループ を使用します。

フォルダー名を丸括弧で囲む(例: (marketing)(dashboard))ことで、そのフォルダー名はURLのパスから除外されます。

具体例

以下のようにフォルダーを構成すると、URLは /login/dashboard になりつつ、それぞれ異なるレイアウトを適用できます。

  • app/(auth)/layout.tsx (認証画面用のシンプルなレイアウト)
  • app/(auth)/login/page.tsx → URLは /login
  • app/(main)/layout.tsx (ヘッダーやサイドナビ付きの標準レイアウト)
  • app/(main)/dashboard/page.tsx → URLは /dashboard

4. コードで見る Layout の基本構造

ルートレイアウト(app/layout.tsx)は、アプリケーションの最上位でHTMLタグやBodyタグを定義する必須のファイルです。

import './globals.css';
import Header from '@/components/Header';
import Footer from '@/components/Footer';

// layoutコンポーネントは children を受け取る必要があります
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body>
        <Header />
        <main>{children}</main> {/* ここに各ページのコンテンツが埋め込まれる */}
        <Footer />
      </body>
    </html>
  );
}

5. Next.js 15/16における重要な変更点:params の非同期化(Promise化)

Next.js 15 以降、layout.tsxpage.tsxroute.tsx 等で受け取る paramssearchParams の型が Promise に変更されました。 これにより、同期的にプロパティにアクセス(例: const slug = params.slug)しようとすると、開発モードで警告またはエラーが発生します。

正しい記述方法 (async / await の利用)

interface PageProps {
  params: Promise<{ slug: string }>;
}

export default async function BlogPostPage({ params }: PageProps) {
  // Promise を await してからプロパティを取り出す
  const { slug } = await params;

  return (
    <div className="p-6">
      <h1 className="text-2xl font-bold">記事スラグ: {slug}</h1>
    </div>
  );
}

まとめ

  • App Router はフォルダ名がルーティングになり、layout.tsxpage.tsx を組み合わせてページを組み立てる。
  • Layout は状態を維持する共通のUI。Template は遷移するたびに状態がリセットされるUI。
  • Next.js 15以降 では、paramssearchParamsPromise となるため、await を使って非同期で取得する必要がある。

レイアウトをネスト(入れ子)にすることで、無駄な再レンダリングを防ぎながら、美しく一貫性のあるユーザーインターフェースを構築できます。次のステップでは、このルーティングと密接に関わるデータ取得(Data Fetching)の仕組みを学んでいきましょう!

理解度チェック

Next.js 15以降において、レイアウトやページコンポーネントで受け取る params や searchParams から値を安全に取得するための正しいアプローチはどれでしょうか?