Next.jsで学ぶReact講座(完全初心者向け・30日) | 第3章:Next.jsらしさ – レイアウト(layout.tsx)

React Next.js
スポンサーリンク

Next.js のレイアウトの全体像

React 単体だと、ページごとにヘッダーやフッターを書きがちです。
でも実際のサイトを想像すると、ほとんどのページで「共通の部分」があります。

画面の一番上にあるヘッダー。
左にあるナビゲーション。
一番下のフッター。

これを毎ページにベタ書きすると、修正が地獄になります。
Next.js の App Router では、この「共通部分」をまとめて管理するために layout.tsx という仕組みが用意されています。
ここに Next.js らしさ がかなり詰まっています。


layout.tsx の役割と仕組み

layout.tsx は「その階層の共通枠」

App Router では、page.tsx が「その URL の中身」、
layout.tsx が「その URL 階層の共通レイアウト」を担当します。

例えば app 直下にある layout.tsx は、全ページ共通の枠になります。
つまり、どのページに行っても共通で表示されるヘッダーやフッターは、ここに書くのが基本です。

イメージとしては、

外側の大きな箱 → layout.tsx
その中に入るページごとの中身 → page.tsx

という二重構造です。

layout.tsx の中では、「子のコンテンツが入る場所」として children を受け取ります。
この children の位置に、その階層の page.tsx やさらに下のレイアウト・ページが差し込まれます。


共通ヘッダーを layout.tsx で作る

最小の layout.tsx の形

Next.js の App Router プロジェクトを作ると、最初から app/layout.tsx が用意されているはずです。
典型的な例は次のような形です(少し簡略化します)。

// app/layout.tsx
import type { ReactNode } from "react";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="ja">
      <body>{children}</body>
    </html>
  );
}
TSX

ここではまだ共通ヘッダーはありませんが、
「全ページ共通で html や body をラップしているレイアウト」が定義されています。

共通ヘッダーを追加してみる

ここに、サイト共通のヘッダーを追加してみます。

// app/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="ja">
      <body style={{ margin: 0, fontFamily: "sans-serif" }}>
        <header
          style={{
            padding: "12px 24px",
            backgroundColor: "#111827",
            color: "white",
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
          }}
        >
          <div>Next.js × React 入門</div>
          <nav style={{ display: "flex", gap: "16px" }}>
            <Link href="/">ホーム</Link>
            <Link href="/about">このサイトについて</Link>
          </nav>
        </header>

        <main style={{ padding: "24px" }}>{children}</main>
      </body>
    </html>
  );
}
TSX

ここで大事なのは children の位置です。
この children の部分に、各ページの page.tsx の内容が差し込まれます。

例えば、

/ にアクセス → app/page.tsx の中身が children に入る。
/about にアクセス → app/about/page.tsx の中身が children に入る。

どちらのページにいても、ヘッダーは layout.tsx によって共通で表示され続けます。


レイアウトの仕組みをもう一段深く理解する

「入れ子のレイアウト」ができるのが強い

App Router のレイアウトは、「階層ごとに layout.tsx を置ける」のがポイントです。

例えば、こんな構成を考えます。

app/layout.tsx
app/page.tsx
app/dashboard/layout.tsx
app/dashboard/page.tsx
app/dashboard/settings/page.tsx

このときレイアウトは次のように入れ子になります。

全ページ共通の枠 → app/layout.tsx
ダッシュボード配下だけの共通枠 → app/dashboard/layout.tsx
その中に各ページの page.tsx が入る

例えば /dashboard の画面は、ざっくり言うと、

RootLayout で全体をラップ
その中に DashboardLayout が入り
さらにその中に /dashboardpage.tsx の中身が入る

という三重構造になります。

ダッシュボード専用レイアウトの例

ダッシュボード配下だけサイドバー付きレイアウトにするイメージです。

// app/dashboard/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";

export default function DashboardLayout({ children }: { children: ReactNode }) {
  return (
    <div style={{ display: "flex", minHeight: "calc(100vh - 60px)" }}>
      <aside
        style={{
          width: "200px",
          padding: "16px",
          borderRight: "1px solid #e5e7eb",
        }}
      >
        <h2>ダッシュボード</h2>
        <nav>
          <p><Link href="/dashboard">概要</Link></p>
          <p><Link href="/dashboard/settings">設定</Link></p>
        </nav>
      </aside>

      <section style={{ flex: 1, padding: "24px" }}>
        {children}
      </section>
    </div>
  );
}
TSX

これを置くだけで、/dashboard/dashboard/settings も、
共通してこのサイドバー付きレイアウトの中で表示されます。

そしてその外側には、さらに app/layout.tsx のヘッダーなどが乗っている、という構造です。


ページ共通化の考え方

「何回も書いているところは layout に追い出す」

レイアウト分割の考え方は、とてもコンポーネント分割に似ています。

同じヘッダーを各ページの page.tsx に毎回書いているなら、それは layout.tsx に移動すべきです。
同じサイドバーをダッシュボード系ページで毎回書いているなら、それは app/dashboard/layout.tsx に切り出すべきです。

つまり、

ページ「ごと」に共通なものはコンポーネント化。
URL階層「全体」で共通なものはレイアウト化。

という感覚です。

layout と「普通のコンポーネント」の違い

どちらも JSX を返す点では同じですが、役割が違います。

普通のコンポーネント
好きな場所から呼び出す「部品」
<Header /><Card /> など

layout.tsx
Next.js によって自動的に使われる「その階層の枠」
children に、その階層のページや下のレイアウトが自動で入る

レイアウトは「この階層全体で使う共通枠」、コンポーネントは「好きなところで使う部品」として、頭の中で整理すると分かりやすいです。


実務での使い所

よくあるレイアウトのパターン

現場レベルでは、だいたいこんな使い方が多いです。

サイト全体共通レイアウト
グローバルヘッダー(ロゴ、グローバルメニュー)
フッター
全体の余白やフォント設定

特定セクションだけのレイアウト
管理画面やダッシュボード用のサイドバー付きレイアウト
マイページエリアだけの特殊なレイアウト

例えば、

一般ユーザー向けページ //about など
会員専用ページ /dashboard 配下

で、レイアウトを分けるのはかなりよくある構成です。

ユーザー側から見ると、「管理画面だけ雰囲気が違うけど、ヘッダーのロゴは共通」みたいなサイトって多いですよね。
それを、app/layout.tsxapp/dashboard/layout.tsx の組み合わせで表現できます。

デザイン変更に強くなる

実務だと、「ヘッダーのデザインを変えたい」「全ページの余白を広くしたい」などの要望は頻繁に出ます。

layout.tsx に共通部分を集約しておけば、

ロゴの位置を変える
共通メニューを追加する
全ページの main の幅を変える

といった修正を、基本的に 1カ所ずつ 直すだけで済みます。

ページごとに同じヘッダーをコピペしている状態だと、
すべてのページを手作業で修正するハメになります。
レイアウトをちゃんと設計しておくことが「後からのコスト」を大きく減らしてくれます。


まとめと一歩先の練習

ここまでのポイントを整理すると、こうなります。

Next.js App Router の layout.tsx は、その階層の「共通レイアウト(枠)」を定義するファイルで、children の位置に各ページの中身が差し込まれる。
app/layout.tsx にヘッダーやフッターを置けば全ページ共通になり、特定のフォルダ内に layout.tsx を置けば、その配下の URL だけに適用されるレイアウトを作れる。
「よく使う部品」は通常のコンポーネントに、「URL 階層全体で共通の枠」は layout に追い出すと、コードが読みやすく、変更に強くなる。
実務では、全体レイアウトとダッシュボード用レイアウトなど、複数の layout を組み合わせて「ユーザー向けエリア」と「管理者向けエリア」を分ける、という使い方がよくある。

練習としては、次のような構成を自分で作ってみるのがおすすめです。

全体レイアウトにサイト共通ヘッダー。
/dashboard 配下だけ、左にメニューがあるレイアウト。
/dashboard/dashboard/settings の2ページを作って行き来してみる。

これが動くようになると、「Next.js らしいレイアウト設計」がかなり体に染みてきます。

タイトルとURLをコピーしました