前回の記事でページとルーティングを覚えました。
ただ、今の状態ではページごとにバラバラなHTMLが表示されるだけです。
実際のWebサイトには、すべてのページに共通のヘッダーやフッターがありますよね。
今回はその「共通部分」を一か所で管理する仕組み、レイアウトを学びます。
あわせて、Server ComponentとClient Componentの使い分けという、Next.jsを理解する上でもっとも重要な概念にも踏み込みます。
Contents
layout.tsx の役割
app/ フォルダに最初から存在する layout.tsx を開いてみましょう。
// app/layout.tsx(初期状態)
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'My App',
description: '...',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>{children}</body>
</html>
);
}
//RootLayout(引数)の部分が読みにくいが以下のように考える
//RootLayout({ children }: { children: React.ReactNode })
// └─ ① ───┘ └────────── ② ──────────┘
//①は分割代入
//②は型の指定
//型定義してみやすくしても良い
// 1. まず、引数の「説明書(型)」を別に定義する
// type Props = {
// children: React.ReactNode;
// };
// 2. 関数側では、その型をシンプルに指定する ↓
// export default function RootLayout({ children }: Props) {
// //省略
// }{children} の部分に、各ページの内容(page.tsx)が流し込まれます。
つまり構造はこうなっています。
layout.tsx
└── {children} ← ここに page.tsx の内容が入る
├── app/page.tsx(/)
├── app/about/page.tsx(/about)
└── app/blog/page.tsx(/blog)layout.tsx に書いたものはすべてのページに共通して表示されます。
ヘッダーやフッターを置く場所はここです。
💡 metadata とは?
一言でいうと、「Googleの検索エンジンや、SNS(XやLINEなど)にページの情報を伝えるための設定」です。
ここに書いた内容は、画面の本文(<body> の中)には表示されません。
ブラウザのタブに表示される文字(title)や、HTMLの裏側に隠れている <head> タグの中に自動的に変換されて埋め込まれます。
ヘッダーとフッターを作る
まず、ヘッダーとフッターをコンポーネントとして別ファイルに切り出します。
コンポーネントを置く場所は app/ ではなく、プロジェクトルートに components/ フォルダを作るのが一般的です。
my-first-nextjs/
├── app/
└── components/ ← ここに共通パーツを置く
├── Header.tsx
└── Footer.tsxHeader.tsx
// components/Header.tsx
import Link from 'next/link';
export default function Header() {
return (
<header style={{ borderBottom: '1px solid #eee', padding: '1rem 2rem' }}>
<nav style={{ display: 'flex', gap: '1.5rem', alignItems: 'center' }}>
<Link href="/" style={{ fontWeight: 'bold', fontSize: '1.2rem' }}>
My Blog
</Link>
<Link href="/blog">ブログ</Link>
<Link href="/about">About</Link>
</nav>
</header>
);
}// components/Footer.tsx
export default function Footer() {
return (
<footer style={{ borderTop: '1px solid #eee', padding: '1rem 2rem', marginTop: '2rem' }}>
<p style={{ color: '#888', fontSize: '0.875rem' }}>
© 2026 My Blog. Built with Next.js.
</p>
</footer>
);
}layout.tsx に組み込む
// app/layout.tsx
import type { Metadata } from 'next';
import Header from '@/components/Header';
import Footer from '@/components/Footer';
export const metadata: Metadata = {
title: 'My Blog',
description: 'Next.jsで作ったブログです。',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<Header />
<main style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
{children}
</main>
<Footer />
</body>
</html>
);
}💡
@/について:
@/components/Headerの@/はプロジェクトルートを指すエイリアスです。
../../components/Headerのような相対パスより読みやすく書けます。
create-next-appで作ったプロジェクトなら最初から使えます。
これだけで、すべてのページにヘッダーとフッターが表示されるようになります。
ネストしたレイアウト
layout.tsx はどのフォルダにも置けます。
たとえば、ブログページだけ別のレイアウトを使いたい場合はこうします。
app/
├── layout.tsx ← 全体のレイアウト(Header・Footer)
└── blog/
├── layout.tsx ← ブログセクション専用のレイアウト
├── page.tsx
└── [id]/
└── page.tsx// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<aside style={{ marginBottom: '1.5rem', padding: '1rem', background: '#f9f9f9' }}>
<p>📝 ブログセクション</p>
</aside>
{children}
</div>
);
}レイアウトはネストして重なります。
/blog 以下のページは、ルートレイアウト(Header・Footer)の中に、このブログレイアウトがさらに入る形になります。
RootLayout(Header + Footer)
└── BlogLayout(サイドバーなど)
└── page.tsx の内容Server ComponentとClient Component
ここからがこの記事のもっとも重要なテーマです。
Next.jsのApp Routerでは、コンポーネントはデフォルトでServer Componentとして動作します。
Server Component(デフォルト)
// Server Component("use client" がないので自動的にこちら)
export default function BlogPost() {
return <article>記事の内容</article>;
}Server Componentはサーバー上でHTMLを生成して、ブラウザに送ります。
ブラウザ側でJavaScriptとして実行されません。
できること:
- データベースやAPIから直接データを取得する(次の記事で詳しく扱います)
- 軽量:ブラウザに余計なJavaScriptを送らない
できないこと:
useState、useEffectなどのReact Hooksが使えない- ボタンクリックなどのイベントを扱えない
windowやdocumentなどブラウザのAPIにアクセスできない
Client Component
ファイルの先頭に "use client" と書くと、そのコンポーネントはClient Componentになります。
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント:{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}Client Componentはブラウザ上でJavaScriptとして動きます。useState や onClick が使えるのはこちらです。
どちらを使えばいいの?
まずServer Componentで考えて、必要になったらClient Componentにするというのが基本方針です。
| やりたいこと | 使うべきコンポーネント |
|---|---|
| データを取得して表示する | Server Component |
| ページのタイトルや静的なテキストを表示する | Server Component |
| ボタンを押したら何かが起きる | Client Component |
| フォームの入力値を管理する | Client Component |
useState / useEffect を使う | Client Component |
実践:ハンバーガーメニューを作る
理解を深めるために、モバイルで開閉するハンバーガーメニューを作ってみましょう。
「クリックしたら開く」という動作が必要なので、Client Componentです。
// components/MobileMenu.tsx
"use client";
import { useState } from 'react';
import Link from 'next/link';
export default function MobileMenu() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? '✕ 閉じる' : '☰ メニュー'}
</button>
{isOpen && (
<nav style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', marginTop: '0.5rem' }}>
<Link href="/" onClick={() => setIsOpen(false)}>トップ</Link>
<Link href="/blog" onClick={() => setIsOpen(false)}>ブログ</Link>
<Link href="/about" onClick={() => setIsOpen(false)}>About</Link>
</nav>
)}
</div>
);
}Header.tsx はServer Componentのままにして、その中にこの Client Component を読み込むことができます。
// components/Header.tsx(Server Component のまま)
import Link from 'next/link';
import MobileMenu from './MobileMenu'; // Client Component を読み込む
export default function Header() {
return (
<header style={{ borderBottom: '1px solid #eee', padding: '1rem 2rem' }}>
{/* デスクトップ用ナビ */}
<nav style={{ display: 'flex', gap: '1.5rem' }}>
<Link href="/" style={{ fontWeight: 'bold' }}>My Blog</Link>
<Link href="/blog">ブログ</Link>
<Link href="/about">About</Link>
</nav>
{/* モバイル用メニュー */}
<MobileMenu />
</header>
);
}ポイント: Server ComponentはClient Componentを子として持てます。
逆(Client ComponentがServer Componentを子として持つ)は原則できません。
「外側がServer、インタラクティブな部分だけClient」という構造が理想的です。
ここまでの全体像
app/
├── layout.tsx ← RootLayout(Header・Footer を含む)
└── blog/
├── layout.tsx ← BlogLayout(ブログ専用)
├── page.tsx
└── [id]/
└── page.tsx
components/
├── Header.tsx ← Server Component
├── Footer.tsx ← Server Component
└── MobileMenu.tsx ← Client Component("use client")まとめ
この記事で学んだこと:
layout.tsxを使うと、全ページ共通のヘッダー・フッターを一か所で管理できる- レイアウトはネストできる。フォルダごとに専用レイアウトを設定可能
- Server Component(デフォルト)はサーバーで動き、データ取得に向いている
- Client Component(
"use client")はブラウザで動き、インタラクションに使う - 「まずServer Component、必要ならClient Component」 が基本方針
次の記事では、Server Componentの強みを活かして、外部APIからデータを取得して表示する方法を学びます。


























