App Router のファイルベースルーティング — Next.js Tips
/ は動いているが /about を足したい、/blog/記事名 のように URL を増やしたい——そんなとき、触るのはほぼ src/app/ 以下のフォルダと page.tsx だけです。App Router ではフォルダ名が URL になり、決まった名前のファイル(layout.tsx など)が役割を持ちます。
「Docker で Next.js の環境を構築する」で my-next-app を用意し、docker compose up -d のあと docker compose ps で Up (healthy) になっている状態を前提にします。Next.js 16 系を想定しています。
参考: Next.js — Project structure · Next.js — Defining Routes
目次
App Router とは
Next.js 13 以降、ルーティングの中心は app/ ディレクトリ(App Router)です。従来の pages/(Pages Router)と違い、フォルダ構造が URL に直結し、レイアウトやローディング UI をファイル名の規約で宣言できます。
| 観点 | Pages Router | App Router |
|---|---|---|
| ルート定義 | pages/about.tsx → /about |
app/about/page.tsx → /about |
| レイアウト | _app.tsx で全体を包む |
各フォルダに layout.tsx |
| デフォルト | クライアント寄り | Server Components がデフォルト |
本記事は App Router 前提です。新規プロジェクトでは App Router を選びましょう。
URL とフォルダの対応(概念図)
flowchart LR subgraph app["src/app/"] page["page.tsx → /"] about["about/page.tsx → /about"] blog["blog/[slug]/page.tsx → /blog/:slug"] end Browser["ブラウザの URL"] --> page Browser --> about Browser --> blog事前準備
| 項目 | 要件 |
|---|---|
| プロジェクト | 「Docker で Next.js の環境を構築する」で作成した my-next-app |
| 開発サーバー | docker compose up -d 後、docker compose ps で Up (healthy)(ログに Ready も確認) |
| エディタ | 任意(src/app/ 配下を編集) |
ホストに Node.js は不要です。ファイルを保存すると、コンテナ内の next dev がホットリロードします。パスは src/app/ 前提です。
最小ルートの作り方
トップページは src/app/page.tsx です。このファイルが / に対応します。
// src/app/page.tsxexport default function HomePage() { return ( <main> <h1>トップページ</h1> <p>App Router の最小ルートです。</p> </main> );}ルートを公開するには、そのフォルダに page.tsx(または page.js)が必須です。layout.tsx だけでは URL は生成されません。
/about を足すには src/app/about/page.tsx を作ります。
// src/app/about/page.tsxexport default function AboutPage() { return ( <main> <h1>About</h1> <p>会社概要ページです。</p> </main> );}保存後、http://localhost:3000/about を開くと表示されます(Docker を再起動する必要は通常ありません)。
特殊ファイル(おさらい)
loading.tsx・error.tsx・route.ts など、決まった名前のファイルも App Router にはあります。一覧は「プロジェクト構成を理解する」で整理済みです。本記事で主に使うのは page.tsx(画面)と layout.tsx(共通枠)です。page.tsx と route.ts は同じフォルダに置けません。UI と API は別フォルダに分けてください。
ネストしたルート
layout.tsx は子ルート全体を包みます。共通ヘッダーを root layout に置く例です。
// src/app/layout.tsximport type { Metadata } from "next";export const metadata: Metadata = { title: "My App",};export default function RootLayout({ children,}: { children: React.ReactNode;}) { return ( <html lang="ja"> <body> <header> <nav> <a href="/">Home</a> | <a href="/about">About</a> </nav> </header> {children} </body> </html> );}src/app/blog/layout.tsx を追加すると、/blog 以下だけ別レイアウトにできます。
// src/app/blog/layout.tsxexport default function BlogLayout({ children,}: { children: React.ReactNode;}) { return ( <section> <h2>ブログ</h2> {children} </section> );}レイアウトはネストされます。/blog/hello を開くと RootLayout → BlogLayout → page の順で描画されます。
動的セグメント
URL の一部をパラメータにするには、フォルダ名を [param] にします。
src/app/blog/[slug]/page.tsx → /blog/my-first-post// src/app/blog/[slug]/page.tsxtype Props = { params: Promise<{ slug: string }>;};export default async function BlogPostPage({ params }: Props) { const { slug } = await params; return ( <main> <h1>記事: {slug}</h1> </main> );}Next.js 15 / 16 系では params が Promise です。await params してから slug を取り出します。
複数セグメントは src/app/shop/[category]/[id]/page.tsx のようにフォルダを重ねます。キャッチオール([...slug])やオプショナル([[...slug]])はここでは扱いません。
手順と確認(Docker)
my-next-app のプロジェクトルートで:
docker compose up -ddocker compose ps期待する結果:
- サービス
webのSTATUSが Up (healthy)(ポート0.0.0.0:3000->3000/tcpなど) - まだ
startingのときは数秒待ってからdocker compose psを再実行するか、docker compose logs webでReadyを確認
healthy になるまで src/app/ の編集に進まないでください。止めている場合は上記で起動してください。
1. ディレクトリをそろえる
src/app/├── layout.tsx # 共通 header(下記コード)├── page.tsx # /├── about/│ └── page.tsx # /about└── blog/ ├── layout.tsx # /blog 以下の見出し └── [slug]/ └── page.tsx # /blog/:slug上記セクションのコードを layout.tsx・page.tsx・about/page.tsx・blog/layout.tsx・blog/[slug]/page.tsx に反映します。
2. 保存してブラウザで確認
| URL | 期待される表示 |
|---|---|
http://localhost:3000/ |
トップページ |
http://localhost:3000/about |
About。上部に Home | About の header |
http://localhost:3000/blog/demo |
「ブログ」見出しの下に 記事: demo |
| 存在しないパス | デフォルトの 404(のちに not-found.tsx を追加可能) |
docker compose のログに GET / 200 や Compiling /about などが出ていれば、配置はおおむね正しいです。画面が変わらないときはブラウザをハードリロードしてください。
3. 止めるとき
# 実行中ターミナルで Ctrl+C のあとdocker compose downトラブルシュート
| 症状 | よくある原因 | 対処 |
|---|---|---|
| 404 になる | page.tsx の名前ミス・配置ミス |
フォルダ名 = URL セグメントか確認 |
| レイアウトが二重 | layout.tsx を深くネストしすぎ |
共通化の範囲を見直す |
params.slug が undefined |
params を await していない |
const { slug } = await params |
| API と page が競合 | 同フォルダに page.tsx と route.ts |
別フォルダに分離 |
| 保存しても反映されない | ブラウザキャッシュ・監視の遅延 | ハードリロード。改善しなければ「Docker で Next.js の環境を構築する」の WATCHPACK_POLLING を確認 |
| 変更が一切拾われない | Compose が止まっている | docker compose up -d → ps で Up (healthy) を確認 |
STATUS が healthy にならない |
起動直後・ビルド失敗 | docker compose logs web を確認。Ready まで待つ |