ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
この記事の要点
layout.tsxは階層ごとに入れ子になり、画面遷移しても再描画されず状態を保つ。ヘッダーやサイドバーの置き場所。loading.tsxを置くと、そのセグメントが自動的に Suspense 境界で包まれ、読み込み中の表示が出る。error.tsxはエラー境界。必ずクライアントコンポーネントにする必要がある。- これらは描画順が決まっている。外側から layout → template → error → loading → not-found → page の順に入れ子になる。
- 遷移のたびに作り直したい枠は
layoutではなくtemplateを使う。
App Router には、ファイル名そのものが役割を持つ「特殊ファイル」がいくつかあります。名前を置くだけで React の Suspense 境界やエラー境界が組み込まれるため、仕組みを知らずに使うと「なぜかローディングが出ない」「エラー画面が効かない」といった状態になりがちです。この記事では各ファイルの役割と、正しい置き場所を整理します。
1特殊ファイルの一覧
| ファイル | 役割 | 置く場所の目安 |
|---|---|---|
layout | 配下ページ共通の外枠。遷移しても状態を保つ | ルート必須。区画ごとに任意 |
page | その URL の本体 | 公開したいすべてのパス |
loading | 読み込み中の表示(Suspense 境界) | データ取得のあるページ |
error | 例外を受け止める境界 | 失敗が想定される区画 |
global-error | ルートレイアウト自体の失敗を受け止める | app/ 直下のみ |
not-found | 404 の表示 | ルート、または区画ごと |
template | 遷移のたびに作り直される外枠 | 入場アニメなどが必要な区画 |
route | API エンドポイント | page と同じ階層には置けない |
default | 並行ルートのフォールバック | @slot を使う場合は必須 |
2レイアウトは入れ子になる
layout.tsx は children を受け取り、その中にページや下位のレイアウトが差し込まれます。フォルダ階層に沿って自動的に入れ子になるため、共通部分を書く場所を階層で選べます。
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<section>
<nav>ブログ内ナビ</nav>
{children}
</section>
)
}
重要なのは、レイアウトは画面遷移しても再描画されないという点です。/blog/a から /blog/b へ移動しても app/blog/layout.tsx はそのまま残り、内部の state やスクロール位置が保たれます。開いたままのアコーディオンや入力途中のフォームがリセットされないのはこのためです。
ルート直下の app/layout.tsx だけはルートレイアウトと呼ばれ、必須かつ html と body を含む必要があります。ルートグループを使えば複数のルートレイアウトを持つこともできますが、その場合はそれぞれに html と body が要ります。
3loading.tsx で読み込み中を見せる
loading.tsx を置くだけで、そのセグメントの page 以下が自動的に Suspense 境界で包まれます。データ取得の完了を待つあいだ、レイアウトは表示したままローディング表示に切り替わります。
// app/blog/loading.tsx
export default function Loading() {
return <p>読み込み中…</p>
}
スピナーよりも、実際の画面に近いスケルトン(見出しやカードの枠だけを描いたもの)のほうが体感は良くなります。ここで一つ落とし穴があります。同じセグメントの layout がデータを取りに行っている場合、その待ち時間は同じ階層の loading では隠せません。レイアウト自身の描画が終わるまで遷移がブロックされるためです。回避するには、レイアウト内のデータ取得部分を個別に Suspense で包むか、取得処理を page 側へ移します。詳しい挙動は ストリーミングで体感速度を上げる で扱います。
4error.tsx はクライアントコンポーネント
error.tsx は、その配下で起きた描画時の例外を受け止めて代替 UI を出します。React のエラー境界そのものなので、ファイル先頭に 'use client' が必須です。これを忘れるとエラー境界として機能しません。
'use client' // エラー境界はクライアントコンポーネントであること
import { useEffect } from 'react'
export default function ErrorPage({
error,
retry,
}: {
error: Error & { digest?: string }
retry: () => void
}) {
useEffect(() => {
console.error(error) // 監視サービスへ送るならここ
}, [error])
return (
<div>
<h2>問題が発生しました</h2>
<button onClick={() => retry()}>再試行</button>
</div>
)
}
受け取る props は error と、その区画をやり直す retry です。エラーは最も近い親のエラー境界まで伝播するので、区画ごとに error.tsx を置けば、画面全体を巻き込まずに一部だけを差し替えられます。
ルートレイアウト自体が壊れた場合はこの仕組みでは受け止められません。そのときに使うのが app/global-error.tsx で、ルートレイアウトを置き換える形で表示されるため、自分で html と body を書く必要があります。
5not-found.tsx で 404 を作る
該当データが無いときは notFound() を呼びます。呼ばれると最も近い not-found.tsx が表示されます。
import { notFound } from 'next/navigation'
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPostBySlug(slug)
if (!post) {
notFound()
}
return <div>{post.title}</div>
}
「見つからない」は障害ではなく正常な結果なので、throw new Error() で表現しないでください。エラー境界に落ちると 500 系の扱いになり、検索エンジンにも意図しない信号を送ることになります。
6layout と template の違い
ヘッダー・サイドバー・タブなど、遷移しても保ったままにしたい枠。state もスクロール位置も維持される。ほとんどの場合はこちら。
遷移ごとにマウントし直したい枠。入場アニメーションを毎回走らせたい、
useEffect を遷移のたびに再実行したい、といった限定的な用途。template.tsx は layout.tsx と同じく children を受け取りますが、遷移のたびに新しいインスタンスが作られます。両方置いた場合は layout の内側に template が入ります。
7つまずきやすいところ
error.tsxが効かない:'use client'が抜けているのが最頻出の原因です。- イベントハンドラ内のエラーが捕まらない:エラー境界は描画中の例外だけを扱います。クリック処理などは
try/catchで捕まえ、useStateに保持して表示を切り替えます。 - ローディングが一瞬も出ない:データ取得が
layout側にあるか、そもそも取得が速すぎるかのどちらかです。 - 全ページに
loading.tsxを置いてしまう:一覧だけに出したい場合は、ルートグループを作ってその中に置くと、URL を変えずに適用範囲を絞れます。
ここまでで画面の骨格が組めました。次はいよいよ App Router の中核である、サーバーとクライアントの境界に進みます。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 Spring Frameworkのアノテーション一覧
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- PHPでのファイルアップロード方法|multipart/form-dataと$_FILES 2026-08-07 17:15:57
- PHP $_SERVERとは|DOCUMENT_ROOT・PHP_SELFなど主な要素 2026-08-07 17:15:57
- Scratchのアカウントの作り方|メールアドレスとユーザー名の登録手順 2026-08-07 17:15:57
- djangoにおけるMVCアプリケーション実装例 2026-08-07 17:15:57
- Bluetoothとは|仕組み・BLEとClassicの違い・用途 2026-08-07 17:15:57
- Spring Bootのテスト入門|@SpringBootTestとJUnitでの単体・結合テスト 2026-08-07 17:15:57
- Java とは?言語仕様・JVM・主要フレームワーク一覧 2026-08-07 17:15:56
- インストール方法(Windows版) 2026-08-07 17:15:56
- APIキー取得方法 2026-08-07 17:15:56
- 【PHPフレームワーク】Laravelの使い方 2026-08-07 17:15:56
- Scratch 作品の公開方法 2026-08-07 17:15:56
- Google OAuth 2.0 認証の実装方法 2026-08-07 17:15:56
- データベースとは?RDB / NoSQL / SQL の基礎 2026-08-07 17:15:56
- インストール(eclipseプラグイン) 2026-08-07 17:15:56
- Spring Frameworkの使い方 2026-08-07 17:15:56
コメントを削除してもよろしいでしょうか?