Next.js ルーティング早見表:セグメント・グループ・オプションの一枚まとめ

Next.js の App Router におけるルーティングは、ファイルシステムベースで直感的な設計になっています。しかし、動的セグメント、ルートグループ、パラレルルート、インターセプトルートなど多彩な機能があり、それぞれの記法や用途を整理しておくと開発がスムーズです。
本記事では、Next.js のルーティングで使える主要な構文と概念を 早見表 形式でまとめ、それぞれの背景・課題・解決策を図解を交えて解説します。
早見表
セグメント早見表
# | 記法 | 例 | URL 例 | 説明 |
---|---|---|---|---|
1 | [segment] | app/blog/[slug]/page.js | /blog/hello-world | 動的セグメント(単一パラメータ) |
2 | [...segment] | app/docs/[...slug]/page.js | /docs/a/b/c | キャッチオールセグメント(複数パラメータ) |
3 | [[...segment]] | app/shop/[[...slug]]/page.js | /shop または /shop/a/b | オプショナルキャッチオール |
グループ・特殊機能早見表
# | 記法 | 例 | 説明 |
---|---|---|---|
4 | (group) | app/(marketing)/page.js | ルートグループ(URL に含まれない論理的なフォルダ分け) |
5 | @folder | app/@modal/page.js | パラレルルート(同一レイアウト内で複数ルートを並行表示) |
6 | (.)folder | app/@modal/(.)photo/[id]/page.js | インターセプトルート(同階層) |
7 | (..)folder | app/@modal/(..)photo/[id]/page.js | インターセプトルート(1 階層上) |
8 | (..)(..)folder | app/@modal/(..)(..)photo/[id]/page.js | インターセプトルート(2 階層上) |
9 | (...)folder | app/@modal/(...)photo/[id]/page.js | インターセプトルート(ルートから) |
ファイル規約早見表
# | ファイル名 | 役割 |
---|---|---|
10 | page.js | ルートの UI を定義(公開ページ) |
11 | layout.js | 複数ページで共有する UI レイアウト |
12 | loading.js | Suspense ベースのローディング UI |
13 | error.js | エラーバウンダリ UI |
14 | not-found.js | 404 Not Found 時の UI |
15 | route.js | API エンドポイント(GET, POST など) |
背景
Next.js は App Router(Next.js 13 以降)を導入し、ファイルシステムベースのルーティングをさらに強化しました。従来の Pages Router では pages/
ディレクトリにファイルを配置するだけでルーティングが完結しましたが、App Router では以下の点が進化しています。
- レイアウトの柔軟性:
layout.js
によるネストされた共有 UI - ストリーミング対応: React Server Components と Suspense の統合
- 並行ルート: 同一レイアウト内で複数のページを並行表示
- インターセプト: モーダルやドロワーなどの UI パターンをルーティングで実現
これにより、複雑な UI 構造を 宣言的かつファイル構造だけで 表現できるようになりました。
次の図は、App Router におけるファイル構造と URL の対応関係を示します。
mermaidflowchart TD
root["app/"]
root --> page1["page.js<br/>(ルートページ: /)"]
root --> blog["blog/"]
blog --> blogPage["page.js<br/>(/blog)"]
blog --> slug["[slug]/"]
slug --> slugPage["page.js<br/>(/blog/:slug)"]
root --> docs["docs/"]
docs --> catchAll["[...slug]/"]
catchAll --> catchPage["page.js<br/>(/docs/a/b/c)"]
上図のように、ディレクトリ構造が URL パスに直接マッピングされます。動的セグメントやキャッチオールセグメントを使うことで、柔軟なルーティングを実現できます。
課題
Next.js のルーティングは強力ですが、多様な記法が存在するため、以下のような課題が生じます。
- 記法の混乱:
[slug]
,[...slug]
,[[...slug]]
の違いがわかりにくい - グループの用途不明:
(group)
がどのような場面で役立つのか理解しにくい - インターセプトの複雑さ:
(.)
,(..)
,(...)
の階層指定が直感的でない - パラレルルートの使い所:
@folder
を使った並行表示の実例が少ない
これらの記法を整理せずに開発を進めると、ファイル構造が肥大化し、メンテナンス性が低下します。
次の図は、ルーティング記法の選択に悩むシーンを示します。
mermaidflowchart LR
dev["開発者"] -->|"どの記法を使う?"| choice{"記法選択"}
choice -->|"固定パス"| static["通常フォルダ"]
choice -->|"1つの動的パラメータ"| dynamic["[slug]"]
choice -->|"複数パラメータ"| catchAll["[...slug]"]
choice -->|"論理的な分類"| group["(group)"]
choice -->|"並行表示"| parallel["@folder"]
choice -->|"モーダルなど"| intercept["(.)folder"]
開発者は状況に応じて適切な記法を選ぶ必要がありますが、選択肢が多いため迷いが生じます。
解決策
Next.js のルーティングを効率的に活用するには、早見表と図解による体系的な理解 が有効です。本記事では以下の方針で整理します。
解決の方針
- 記法を分類: セグメント、グループ、特殊機能の 3 つに分ける
- 用途を明確化: それぞれの記法が解決する問題を示す
- 実例を提示: 具体的なファイル構造と URL の対応を示す
- 図解で理解: フローチャートやディレクトリツリーで視覚化
次の図は、ルーティング記法の分類と用途をまとめたものです。
mermaidflowchart TD
routing["Next.js<br/>ルーティング"]
routing --> segment["セグメント"]
routing --> group_feature["グループ・<br/>特殊機能"]
routing --> file_conv["ファイル規約"]
segment --> static_seg["通常フォルダ"]
segment --> dynamic_seg["[slug]"]
segment --> catch_seg["[...slug]"]
segment --> optional_seg["[[...slug]]"]
group_feature --> route_group["(group)"]
group_feature --> parallel["@folder"]
group_feature --> intercept["(.)folder"]
file_conv --> page_file["page.js"]
file_conv --> layout_file["layout.js"]
file_conv --> loading_file["loading.js"]
file_conv --> error_file["error.js"]
file_conv --> route_file["route.js"]
この分類により、記法の役割が明確になり、適切な選択ができるようになります。
具体例
以下では、各記法の具体的な使い方をコード例と共に解説します。
動的セグメント [slug]
動的セグメントは、単一のパラメータを URL から受け取る場合に使用します。ブログ記事の個別ページなどで活用されます。
ディレクトリ構造
textapp/
blog/
[slug]/
page.js
URL 例
/blog/hello-world
/blog/nextjs-routing
コード例(page.js)
typescript// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
// params.slug に "hello-world" などが入る
return (
<article>
<h1>{params.slug} の記事</h1>
<p>
動的セグメントで取得したスラッグを表示しています。
</p>
</article>
);
}
上記のコードでは、params.slug
を使って URL パラメータを取得しています。
静的生成との組み合わせ
typescript// app/blog/[slug]/page.js
// 静的生成時にパスを事前生成
export async function generateStaticParams() {
// データソースから記事一覧を取得
const posts = await fetch(
'https://api.example.com/posts'
).then((res) => res.json());
// slug の配列を返す
return posts.map((post) => ({
slug: post.slug,
}));
}
export default function BlogPost({ params }) {
return <h1>{params.slug}</h1>;
}
generateStaticParams
を使うことで、ビルド時に全ページを事前生成できます。
キャッチオールセグメント [...slug]
複数の階層を持つパスを一括で受け取りたい場合に使用します。ドキュメントサイトの階層構造などに便利です。
ディレクトリ構造
textapp/
docs/
[...slug]/
page.js
URL 例
/docs/getting-started
/docs/api/routing/dynamic
コード例(page.js)
typescript// app/docs/[...slug]/page.js
export default function DocsPage({ params }) {
// params.slug は配列になる
// /docs/api/routing → ["api", "routing"]
const pathSegments = params.slug.join(' / ');
return (
<div>
<h1>ドキュメント</h1>
<p>現在のパス: {pathSegments}</p>
</div>
);
}
params.slug
は配列形式で渡されるため、join
などで整形できます。
オプショナルキャッチオール [[...slug]]
キャッチオールセグメントに加え、セグメントが存在しない場合も同じページで対応できます。
ディレクトリ構造
textapp/
shop/
[[...slug]]/
page.js
URL 例
/shop
→params.slug
はundefined
/shop/electronics
→params.slug
は["electronics"]
/shop/electronics/phones
→params.slug
は["electronics", "phones"]
コード例(page.js)
typescript// app/shop/[[...slug]]/page.js
export default function ShopPage({ params }) {
const category = params.slug
? params.slug.join(' > ')
: 'すべて';
return (
<div>
<h1>ショップ</h1>
<p>カテゴリ: {category}</p>
</div>
);
}
オプショナルキャッチオールを使うことで、トップページと階層ページを一つのコンポーネントで管理できます。
ルートグループ (group)
ルートグループは、URL に影響を与えずにファイルを論理的に整理するための仕組みです。マーケティングページと管理画面でレイアウトを分けたい場合などに有効です。
ディレクトリ構造
textapp/
(marketing)/
about/
page.js
contact/
page.js
layout.js
(dashboard)/
profile/
page.js
settings/
page.js
layout.js
URL 例
/about
→(marketing)
は URL に含まれない/profile
→(dashboard)
は URL に含まれない
コード例(layout.js)
typescript// app/(marketing)/layout.js
export default function MarketingLayout({ children }) {
return (
<div>
<header>マーケティングヘッダー</header>
{children}
<footer>マーケティングフッター</footer>
</div>
);
}
typescript// app/(dashboard)/layout.js
export default function DashboardLayout({ children }) {
return (
<div>
<aside>ダッシュボードサイドバー</aside>
<main>{children}</main>
</div>
);
}
ルートグループを使うことで、同じ app/
配下でも異なるレイアウトを適用できます。
次の図は、ルートグループによるレイアウト分離を示します。
mermaidflowchart TD
app["app/"]
app --> marketing["(marketing)/"]
app --> dashboard["(dashboard)/"]
marketing --> mLayout["layout.js<br/>(マーケティング用)"]
marketing --> about["about/page.js<br/>URL: /about"]
marketing --> contact["contact/page.js<br/>URL: /contact"]
dashboard --> dLayout["layout.js<br/>(ダッシュボード用)"]
dashboard --> profile["profile/page.js<br/>URL: /profile"]
dashboard --> settings["settings/page.js<br/>URL: /settings"]
このように、グループごとに異なる layout.js
を配置することで、UI 構造を柔軟に管理できます。
パラレルルート @folder
パラレルルートは、同じレイアウト内で複数のページを並行して表示する機能です。ダッシュボードのように、サイドバーとメインコンテンツを別々のルートで管理したい場合に使用します。
ディレクトリ構造
textapp/
dashboard/
@sidebar/
page.js
@main/
page.js
layout.js
コード例(layout.js)
typescript// app/dashboard/layout.js
export default function DashboardLayout({ sidebar, main }) {
return (
<div style={{ display: 'flex' }}>
{/* @sidebar の内容 */}
<aside>{sidebar}</aside>
{/* @main の内容 */}
<main>{main}</main>
</div>
);
}
コード例(@sidebar/page.js)
typescript// app/dashboard/@sidebar/page.js
export default function Sidebar() {
return (
<nav>
<ul>
<li>メニュー 1</li>
<li>メニュー 2</li>
</ul>
</nav>
);
}
コード例(@main/page.js)
typescript// app/dashboard/@main/page.js
export default function Main() {
return (
<div>
<h1>ダッシュボード</h1>
<p>メインコンテンツを表示します。</p>
</div>
);
}
パラレルルートを使うことで、複数のコンポーネントを独立したルートとして管理でき、コードの分離が進みます。
インターセプトルート (.)folder
インターセプトルートは、特定の URL にアクセスした際に、別のページをモーダルやオーバーレイで表示する機能です。画像ギャラリーで、サムネイルクリック時にモーダルで拡大表示する UI を実現できます。
ディレクトリ構造
textapp/
photos/
[id]/
page.js
@modal/
(.)photos/
[id]/
page.js
layout.js
インターセプト階層の記法
記法 | 意味 |
---|---|
(.)folder | 同じ階層 |
(..)folder | 1 つ上の階層 |
(..)(..)folder | 2 つ上の階層 |
(...)folder | ルート(app/)から |
コード例(layout.js)
typescript// app/layout.js
export default function RootLayout({ children, modal }) {
return (
<html>
<body>
{children}
{/* モーダルが存在する場合のみ表示 */}
{modal}
</body>
</html>
);
}
コード例(@modal/(.)photos/[id]/page.js)
typescript// app/@modal/(.)photos/[id]/page.js
export default function PhotoModal({ params }) {
return (
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
background: 'rgba(0,0,0,0.8)',
}}
>
<div
style={{
background: 'white',
padding: '2rem',
margin: '5rem auto',
maxWidth: '600px',
}}
>
<h2>写真 {params.id}</h2>
<p>モーダル表示中</p>
</div>
</div>
);
}
コード例(photos/[id]/page.js)
typescript// app/photos/[id]/page.js
export default function PhotoPage({ params }) {
return (
<div>
<h1>写真 {params.id}</h1>
<p>通常ページとして表示</p>
</div>
);
}
インターセプトルートを使うことで、同じ URL でもナビゲーションの文脈に応じて異なる UI を表示できます。
次の図は、インターセプトルートの動作フローを示します。
mermaidflowchart LR
user["ユーザー"] -->|"クリック"| gallery["ギャラリーページ"]
gallery -->|"内部遷移"| intercept["@modal/(.)photos/[id]<br/>(モーダル表示)"]
user -->|"直接アクセス"| direct["photos/[id]<br/>(通常ページ)"]
内部遷移ではモーダル、直接アクセスでは通常ページが表示されるという使い分けができます。
ファイル規約の活用
App Router では、特定のファイル名に特別な役割が割り当てられています。
page.js
ルートとして公開される UI を定義します。このファイルがないディレクトリは URL としてアクセスできません。
typescript// app/about/page.js
export default function AboutPage() {
return <h1>About ページ</h1>;
}
layout.js
複数のページで共有する UI レイアウトを定義します。ネストされた layout.js
は親のレイアウトを継承します。
typescript// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang='ja'>
<body>
<header>共通ヘッダー</header>
{children}
<footer>共通フッター</footer>
</body>
</html>
);
}
loading.js
React Suspense を使ったローディング UI を定義します。ページのデータ取得中に表示されます。
typescript// app/dashboard/loading.js
export default function Loading() {
return <p>読み込み中...</p>;
}
error.js
エラーバウンダリとして機能し、子コンポーネントでエラーが発生した場合に表示されます。
typescript// app/dashboard/error.js
'use client';
export default function Error({ error, reset }) {
return (
<div>
<h2>エラーが発生しました</h2>
<p>{error.message}</p>
<button onClick={() => reset()}>再試行</button>
</div>
);
}
エラーバウンダリはクライアントコンポーネントである必要があるため、'use client'
を指定します。
not-found.js
404 Not Found 時の UI を定義します。
typescript// app/not-found.js
export default function NotFound() {
return (
<div>
<h1>404 - ページが見つかりません</h1>
<p>お探しのページは存在しません。</p>
</div>
);
}
route.js
API エンドポイントを定義します。page.js
と同じディレクトリには配置できません。
typescript// app/api/posts/route.js
export async function GET() {
const posts = [
{ id: 1, title: 'Hello World' },
{ id: 2, title: 'Next.js Routing' },
];
return Response.json(posts);
}
typescript// app/api/posts/route.js
export async function POST(request) {
const body = await request.json();
// データベースに保存する処理
return Response.json({
message: '作成しました',
data: body,
});
}
route.js
を使うことで、Next.js アプリ内に API を簡単に構築できます。
まとめ
Next.js の App Router におけるルーティングは、ファイルシステムベースの直感的な設計と、柔軟な機能の組み合わせにより、複雑な UI 構造を宣言的に表現できます。
本記事で紹介した内容を以下にまとめます。
- 動的セグメント (
[slug]
,[...slug]
,[[...slug]]
) を使い分けることで、柔軟なパラメータ取得が可能 - ルートグループ (
(group)
) により、URL に影響を与えずにファイルを論理的に整理 - パラレルルート (
@folder
) で、同一レイアウト内に複数のルートを並行表示 - インターセプトルート (
(.)folder
) により、モーダルやオーバーレイなどの UI パターンをルーティングで実現 - ファイル規約 (
page.js
,layout.js
,loading.js
,error.js
,route.js
など) を活用し、宣言的な UI 構築
これらの機能を理解し、早見表を参照しながら開発を進めることで、メンテナンス性が高く、拡張しやすい Next.js アプリケーションを構築できるでしょう。
関連リンク
- article
Next.js ルーティング早見表:セグメント・グループ・オプションの一枚まとめ
- article
Next.js × pnpm/Turborepo 初期構築:ワークスペース・共有パッケージ・CI 最適化
- article
Next.js Route Handlers vs API Routes:設計意図・性能・制約のリアル比較
- article
Remix と Next.js/Vite/徹底比較:選ぶべきポイントはここだ!
- article
【実測検証】Remix vs Next.js vs Astro:TTFB/LCP/開発体験を総合比較
- article
Next.js で「Dynamic server usage: cookies/headers」はなぜ起きる?原因と解決手順
- article
NestJS クリーンアーキテクチャ:UseCase/Domain/Adapter を疎結合に保つ設計術
- article
WebSocket プロトコル設計:バージョン交渉・機能フラグ・後方互換のパターン
- article
MySQL 読み書き分離設計:ProxySQL で一貫性とスループットを両立
- article
Motion(旧 Framer Motion)アニメオーケストレーション設計:timeline・遅延・相互依存の整理術
- article
WebRTC で遠隔支援:画面注釈・ポインタ共有・低遅延音声の実装事例
- article
JavaScript パフォーマンス最適化大全:レイアウトスラッシングを潰す実践テク
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来