言語を切り替える
テーマを切り替える

Next.js 高度なルーティング実践:Route Groups・ネストレイアウト・Parallel Routes・Intercepting Routes 完全ガイド

先週、2 年運用の Next.js EC プロジェクトを引き継ぎました。app ディレクトリを開いた瞬間、画面上に 60 以上のフォルダがぎっしり——aboutproductsadmin-usersmarketing-campaignshop-cart……すべてフラットに並んでいます。ユーザー関連ページを探すのに、マーケページの山を掘り返す羽目に。さらに辛いのは、3 人が同時にルートファイルを触り、Git 競合が毎日 2 件、コードレビューだけでファイル関係の整理に 30 分かかることでした。

モニターの前でフォルダの山を見ながら、ふと思い出しました。Next.js には Route Groups や Parallel Routes といった高度なルーティングがあるはずだ、と。公式ドキュメントをめくると、Next.js 13 からずっとある機能なのに、プロジェクトでは一度も使われていませんでした。ツールが足りないのではなく、いつ・どれを使うかがわかっていなかっただけです。

その夜 3 時間で機能を学び直し、2 日かけて構造をリファクタリング。ディレクトリは一気にすっきりし、チームチャットから「また競合した」は消えました。そして 4 つの機能が何を解決するのかはっきりしました。Route Groups で整理、ネストレイアウト で構造を再利用、Parallel Routes で複数画面を同時表示、Intercepting Routes でモーダルを自然に実装する、と。

Next.js プロジェクトも肥大化し、ファイルが散らかり、チーム協業でいつもぶつかる——そんな方に向けて、どの機能をいつどう使うか、落とし穴も含めて整理します。

Route Groups(ルートグループ)— ディレクトリをすっきり

Route Groups とは?

結論から:丸括弧で包んだフォルダ、例 (marketing)(shop) です。Next.js は括弧内の名前を URL に含めません

一見地味ですが、現場では効きます。

EC サイトにマーケページ(トップ、About)、ショップ(商品一覧、カート)、管理画面(注文、ユーザー)があるとします。従来は app 直下に詰め込むか、無理に /marketing/about のような URL 階層を付けます。でも yoursite.com/marketing/about なんて見せたくないですよね。

Route Groups なら ファイル上だけ分類でき、ユーザーが見る URL はそのままです。

3 つの核心的な使い道

使い道 1:チーム・機能ごとにディレクトリ分割

60 個のフラットフォルダを 3 グループに:

app/
├── (marketing)/    # マーケチーム
│   ├── page.js     # トップ → yoursite.com/
│   ├── about/      # → yoursite.com/about
│   └── pricing/
├── (shop)/         # フロント
│   ├── products/
│   └── cart/
└── (dashboard)/    # バックエンド
    ├── orders/
    └── users/

URL は以前と同じくきれいで、構造は一目瞭然。新人もどこを触るか迷いません。

使い道 2:エリアごとに異なるルートレイアウト

マーケと管理画面で同じナビは無理です。でも /about/orders はどちらもルート直下。解は 各 Route Group に独自の layout.js です。

app/
├── (marketing)/
│   ├── layout.js        # マーケ:大きなヒーロー + ナビ
│   └── ...
├── (shop)/
│   ├── layout.js        # ショップ:カートアイコン + カテゴリ
│   └── ...
└── (dashboard)/
    ├── layout.js        # 管理:サイドバー + 権限チェック
    └── ...

1 プロジェクトで 3 種類の「アプリ感」を、サブドメイン分割なしで実現できます。

使い道 3:一部のページだけレイアウトを共有

ブログ記事だけ左ナビ、トップにはナビなし、といった要件:

app/
├── blog/
│   ├── page.js         # リスト(サイドバーなし)
│   └── (articles)/
│       ├── layout.js   # 記事グループ共通のサイドバー
│       ├── [slug]/
│       └── ...

(articles) は URL に出ません。パスは /blog/my-first-post のまま、このグループ内だけ専用 layout が効きます。

リファクタリング実例

Before(抜粋):

app/
├── page.js
├── about/
├── pricing/
├── products/
├── cart/
├── admin-orders/
├── admin-users/
├── marketing-campaign/
├── ...(あと 50)

After:

app/
├── (marketing)/
│   ├── layout.js
│   ├── page.js
│   ├── about/
│   └── pricing/
├── (shop)/
│   ├── layout.js
│   ├── products/
│   └── cart/
└── (dashboard)/
    ├── layout.js
    ├── orders/
    └── users/

3 層で整理。マーケは (marketing)、管理は (dashboard)。レビューは担当グループだけ見ればよく、競合率は約 70% 減しました。

3 つの落とし穴

落とし穴 1:URL 衝突

app/
├── (marketing)/
│   └── about/page.js   # → /about
└── (shop)/
    └── about/page.js   # → /about(衝突)

Error: Conflicting route になります。パス名を変えるか、括弧なしの実ディレクトリで階層を分けます。

落とし穴 2:異なるルートレイアウト間はフルリロード

(shop) から (marketing) へ遷移すると画面が「フラッシュ」します。ルート layout が別物なので、旧 layout のアンマウントと新 layout のマウントが必要です。SPA 的な滑らかさが欲しければ、共通部分は app/layout.js に上げ、グループ内 layout は差分だけにします。

落とし穴 3:トップ page.js の置き場所

複数グループにそれぞれ layout.js があるとき、トップの page.jsいずれか 1 グループ内に置きます(例:(marketing)/page.js)。app/page.js だけだとどのルート layout を使うか曖昧になります。


Route Groups の本質は 整理のための仕組みで、URL は変えず、エリアごとに layout を分けられる こと。app に 20 フォルダを超えたら、試す価値があります。

ネストレイアウト(Nested Layouts)— ページ構造の再利用

何を解決するか?

トップはヘッダーのみ、ブログ一覧はヘッダー + 左カテゴリ、記事詳細はヘッダー + 左カテゴリ + 右目次——従来は各ページでコンポーネントを手組み。ナビのスタイル変更は 3 箇所。

ネストレイアウトは マトリョーシカのように layout を重ね、外側が内側すべてに自動適用します。深い階層ほど、その層だけの UI を足せます。

仕組み

各フォルダに layout.js を置くと、子は親 layout を継承し、その上に自分の shell を重ねます。

オンライン学習サイトの例:

app/
├── layout.js              # ヘッダー + Footer
└── courses/
    ├── layout.js          # + 左:コース一覧
    ├── page.js
    └── [id]/
        ├── layout.js      # + 右:進捗バー
        └── page.js

/courses/123 の描画順:

  1. app/layout.js(ヘッダー + Footer)
  2. courses/layout.js(左カテゴリ)
  3. courses/[id]/layout.js(右進捗)
  4. courses/[id]/page.js(本文)

ヘッダーを直すなら app/layout.js だけで全ページに反映されます。

実践:ブログの 3 層 layout

  • 全ページ:Header + Footer
  • ブログ系:+ 左カテゴリ
  • 記事詳細:+ 右アンカー目次
app/
├── layout.js
└── blog/
    ├── layout.js
    ├── page.js
    └── [slug]/
        ├── layout.js
        └── page.js

app/layout.js

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Header />
        {children}
        <Footer />
      </body>
    </html>
  )
}

app/blog/layout.js

export default function BlogLayout({ children }) {
  return (
    <div className="blog-container">
      <Sidebar />
      <main>{children}</main>
    </div>
  )
}

app/blog/[slug]/layout.js

export default function ArticleLayout({ children }) {
  return (
    <div className="article-container">
      {children}
      <TableOfContents />
    </div>
  )
}

各 layout は 自分が足す UI だけ を書けばよく、Next.js が順にネストします。

Route Groups との組み合わせ

Route Groups は横方向の分離(マーケ/ショップ/管理)、ネストレイアウトは縦方向の UI 積み上げです。

EC のショップ領域例:

app/
└── (shop)/
    ├── layout.js             # カートアイコン + カテゴリナビ
    ├── products/
    │   ├── layout.js         # + 左フィルター
    │   ├── page.js
    │   └── [id]/
    │       ├── layout.js     # + パンくず
    │       └── page.js
    └── cart/
        └── page.js           # ショップ根 layout のみ
  • /products:根 + フィルター
  • /products/123:根 + フィルター + パンくず
  • /cart:根のみ

「詳細だけパンくず」みたいな if 分岐をページに書かず、ディレクトリ階層で決まります。

2 つの注意点

注意 1:layout は子ルート切り替えで再レンダリングされない

/blog/blog/my-post では app/layout.jsblog/layout.js は維持され、最内の page.js だけ更新。サイドバーのスクロールや検索欄の入力も保持されます。

注意 2:layout は子の動的 params を直接取れない

app/products/[id]/page.js[id]page.jsparams で取得。layout で商品タイトルを出したいなら Context やデータのリフトアップが必要です。


ネストレイアウトは 見た目の外側から内側の層数=フォルダのネスト数 に揃える考え方。コピペを減らし、1 つの layout.js でエリア全体を更新できます。

Parallel Routes(パラレルルート)— 複数ページを同時表示

どんな場面で使う?

管理画面ダッシュボードで同時に:

  • 左上:売上チャート
  • 右上:最新注文
  • 下:在庫アラート

データ源も速度もバラバラ。従来は dashboard/page.js で 3 API を待ち、1 つ落ちると全体が死ぬ。モジュール別 loading も state が増殖します。

Parallel Routes は 1 画面内で 複数の独立した「ページ断片」(スロット) を同時レンダリング。各スロットに loading・error・ナビを持てます。

構文:@ でスロット定義

@folder がスロット名です。

app/
└── dashboard/
    ├── layout.js
    ├── @sales/
    │   └── page.js
    ├── @orders/
    │   └── page.js
    ├── @inventory/
    │   └── page.js
    └── page.js

layout.js で props として受け取ります:

export default function DashboardLayout({
  children,
  sales,
  orders,
  inventory
}) {
  return (
    <div className="dashboard">
      <div className="widgets">
        <div className="widget">{sales}</div>
        <div className="widget">{orders}</div>
      </div>
      <div className="main">{children}</div>
      <div className="alerts">{inventory}</div>
    </div>
  )
}

3 スロットは独立した「子ページ」のように配置できます。

実践:ダッシュボード

@sales/page.js

async function getSalesData() {
  const res = await fetch('https://api.example.com/sales')
  return res.json()
}

export default async function SalesWidget() {
  const data = await getSalesData()
  return (
    <div>
      <h3>今月の売上</h3>
      <Chart data={data} />
    </div>
  )
}

@orders/page.js

async function getRecentOrders() {
  const res = await fetch('https://api.example.com/orders')
  return res.json()
}

export default async function OrdersWidget() {
  const orders = await getRecentOrders()
  return (
    <div>
      <h3>最新注文</h3>
      <ul>
        {orders.map(order => <li key={order.id}>{order.title}</li>)}
      </ul>
    </div>
  )
}

@inventory/page.js

export default function InventoryWidget() {
  return (
    <div>
      <h3>在庫アラート</h3>
      <p>在庫不足 5 件</p>
    </div>
  )
}

3 スロットは並列ロード。注文が先に出て、売上が遅れても他は表示。1 スロットだけエラーならそこだけ失敗表示。

独立した loading / error

app/
└── dashboard/
    ├── @sales/
    │   ├── page.js
    │   ├── loading.js
    │   └── error.js
    ├── @orders/
    │   ├── page.js
    │   └── loading.js
    └── @inventory/
        └── page.js

@sales/loading.js

export default function SalesLoading() {
  return <div>売上データを読み込み中...</div>
}

@sales/error.js

'use client'

export default function SalesError({ error, reset }) {
  return (
    <div>
      <p>売上データの読み込みに失敗しました</p>
      <button onClick={reset}>再試行</button>
    </div>
  )
}

売上だけ「読み込み中」、注文は先に表示——白画面待ちが減ります。

default.js の役割

/dashboard から /dashboard/settings へ移ると、@sales などは /dashboard/settings にマッチしません。デフォルトは 前のスロット内容を維持(SPA 的な部分更新)。

切り替え時にスロットを空にしたいときは default.js

app/
└── dashboard/
    ├── @sales/
    │   ├── page.js
    │   └── default.js
    └── ...
export default function SalesDefault() {
  return null
}

Parallel Routes は Intercepting Routes と組み合わせたモーダル でよく使われますが、ダッシュボード単体でも威力があります。

Intercepting Routes(インターセプトルート)— モーダルを自然に

Instagram 的な画像閲覧

Feed で画像タップ → モーダル表示、URL は /photo/abc123。しかし:

  • 戻る → モーダルが閉じ Feed に戻る(別ページ履歴にならない)
  • リロード → フルの詳細ページ
  • URL 共有 → 相手はフルページ(モーダルではない)

従来は state・history・URL 解析が地獄。Intercepting Routesクライアント遷移を横取りしてモーダル表示、直アクセス・リロードではフルページ です。

構文:(.) など

  • (.) — 同階層を横取り
  • (..) — 1 つ上
  • (..)(..) — 2 つ上
  • (...)app ルートから

例:/photos 一覧から /photos/123 へ。クリック時はモーダル、直アクセスはフルページ。

app/
├── @modal/
│   ├── (.)photos/
│   │   └── [id]/
│   │       └── page.js
│   └── default.js
├── layout.js
├── page.js
└── photos/
    └── [id]/
        └── page.js

@modal/(.)photos/@modal と同階層の photos を横取りします。

実践:Instagram 風画像モーダル

app/layout.js

export default function RootLayout({ children, modal }) {
  return (
    <html>
      <body>
        {children}
        {modal}
      </body>
    </html>
  )
}

app/page.js(グリッド)

import Link from 'next/link'

const photos = [
  { id: '1', url: '/images/photo1.jpg' },
  { id: '2', url: '/images/photo2.jpg' },
]

export default function HomePage() {
  return (
    <div className="photo-grid">
      {photos.map(photo => (
        <Link key={photo.id} href={`/photos/${photo.id}`}>
          <img src={photo.url} alt="" />
        </Link>
      ))}
    </div>
  )
}

app/@modal/(.)photos/[id]/page.js

'use client'

import { useRouter } from 'next/navigation'
import Image from 'next/image'

export default function PhotoModal({ params }) {
  const router = useRouter()

  return (
    <div className="modal-backdrop" onClick={() => router.back()}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        <button onClick={() => router.back()}>閉じる</button>
        <Image src={`/images/photo${params.id}.jpg`} fill />
      </div>
    </div>
  )
}

app/photos/[id]/page.js(フルページ)

import Image from 'next/image'

export default function PhotoPage({ params }) {
  return (
    <div className="photo-page">
      <nav>ホームへ</nav>
      <h1>画像詳細</h1>
      <Image src={`/images/photo${params.id}.jpg`} width={800} height={600} />
      <p>説明文...</p>
    </div>
  )
}

app/@modal/default.js

export default function Default() {
  return null
}

体験の流れ

  1. ホームで画像クリック — クライアント遷移で /photos/1@modal/(.)photos/[id] が横取りしモーダル表示。URL だけ変わりフルリロードなし。
  2. 戻るrouter.back()/ に戻り、default.js でモーダル消去。Feed はそのまま。
  3. リロード/直アクセス — 横取りなし、photos/[id]/page.js のフルページ。
  4. 共有 — 相手もフルページ。

横取りレベルの選び方

app/@modal/ から見た相対位置で決めます。

  • ターゲット app/photos/(同階層)→ (.)photos
  • app/shop/products/(1 つ上の子)→ (..)products など
  • 深いネストで迷ったら → まず (...)
app/
└── shop/
    ├── @modal/
    │   └── (..)products/
    │       └── [id]/
    └── products/
        └── [id]/

3 つの落とし穴

落とし穴 1:default.js 忘れ — モーダルが残り続ける。null を返す default.js 必須。

落とし穴 2:Server Component で useRouter — モーダル閉じは 'use client' が必要。

落とし穴 3:深いネストで横取りパスミス — わからなければ (...) でまず動かす。


Intercepting Routes + Parallel Routes で URL 共有可能・リロードでフルページ・戻るでモーダル閉じ が揃います。Instagram、X、Airbnb と同系統の UX を Next.js で再現できます。

総合実践 — 4 技術の組み合わせ

EC プラットフォームの完全構造

4 章を単体で見ると「便利」程度ですが、組み合わせたときに真価があります。中型 EC の要件で、Route Groups・ネストレイアウト・Parallel Routes・Intercepting Routes を全部載せた構造を見ます。

要件

3 機能エリア(layout が別):

  • マーケ//about/pricing):大きなビジュアル + シンプルナビ
  • ショップ/products/cart):固定カート + カテゴリナビ
  • 管理/dashboard):サイドバー + 権限

ショップの細部

  • 一覧:左フィルター
  • 詳細:パンくず
  • カードクリック:一覧のままクイックプレビューモーダル
  • リロード/直 URL:フル詳細

管理ダッシュボード

  • 売上・注文・在庫を同時表示
  • モジュール別 loading / error

ディレクトリ構造

app/
├── layout.js
├── (marketing)/
│   ├── layout.js
│   ├── page.js
│   ├── about/
│   └── pricing/
├── (shop)/
│   ├── layout.js
│   ├── @modal/
│   │   ├── (.)products/
│   │   │   └── [id]/
│   │   │       └── page.js
│   │   └── default.js
│   ├── products/
│   │   ├── layout.js
│   │   ├── page.js
│   │   └── [id]/
│   │       ├── layout.js
│   │       └── page.js
│   └── cart/
│       └── page.js
└── (dashboard)/
    ├── layout.js
    ├── @sales/
    │   ├── page.js
    │   └── loading.js
    ├── @orders/
    │   ├── page.js
    │   └── loading.js
    ├── @inventory/
    │   └── page.js
    └── page.js
  • Route Groups — 3 エリア分離
  • ネストレイアウト — ショップ内で UI を段階追加
  • Parallel Routes@modal@sales など
  • Intercepting Routes — 商品クイックプレビュー

要点コード

app/(shop)/layout.js

export default function ShopLayout({ children, modal }) {
  return (
    <div>
      <nav>{/* カート + カテゴリ */}</nav>
      {children}
      {modal}
    </div>
  )
}

app/(shop)/products/layout.js

export default function ProductsLayout({ children }) {
  return (
    <div className="products-container">
      <aside>{/* フィルター */}</aside>
      <main>{children}</main>
    </div>
  )
}

app/(shop)/products/page.js

import Link from 'next/link'

export default function ProductsPage() {
  return (
    <div className="product-grid">
      {products.map(p => (
        <Link key={p.id} href={`/products/${p.id}`}>
          <ProductCard product={p} />
        </Link>
      ))}
    </div>
  )
}

カードクリック → クライアント遷移 → 横取り → モーダル。

app/(shop)/@modal/(.)products/[id]/page.js

'use client'

import { useRouter } from 'next/navigation'
import Link from 'next/link'

export default function ProductModal({ params }) {
  const router = useRouter()

  return (
    <div className="modal-backdrop" onClick={() => router.back()}>
      <div className="modal">
        <h2>クイックプレビュー</h2>
        <ProductPreview id={params.id} />
        <Link href={`/products/${params.id}`} onClick={() => router.back()}>
          詳細を見る
        </Link>
      </div>
    </div>
  )
}

app/(dashboard)/layout.js

export default function DashboardLayout({ children, sales, orders, inventory }) {
  return (
    <div className="dashboard">
      <aside>{/* サイドバー */}</aside>
      <main>
        {children}
        <div className="widgets">
          <div className="widget">{sales}</div>
          <div className="widget">{orders}</div>
          <div className="widget">{inventory}</div>
        </div>
      </main>
    </div>
  )
}

設計の理由

Route Groups — ナビが全く違う 3 エリアを分離し、チームごとに担当ディレクトリを分けて競合を減らす。

ネストレイアウト — 一覧にフィルター、詳細にパンくず、どちらもショップ共通ヘッダーを継承。UI とフォルダ階層を一致。

Intercepting Routes + Parallel Routes — 一覧から離れずプレビューしつつ URL は /products/123 で共有可能。リロードはフル詳細。

Parallel Routes(管理) — データ源・速度が違う 3 ウィジェットを独立ロード・独立エラー処理。

チームでの効果(実測感)

  • 競合約 65% 減 — フロントは (shop)、バックエンドは (dashboard)
  • オンボーディング半分 — ツリーを見れば担当がわかる
  • 保守コスト低下 — ナビ変更は該当グループの layout.js のみ
  • UX — クイックプレビューで詳細ページ直行よりコンバージョン約 23% 向上(戻る操作が減ったため)

4 機能は「派手技」ではなく、構造・協業・UX を整えるためのもの。小規模なら不要ですが、ルートが数十、複数チーム、モーダル UX があるなら効きます。

結論

冒頭の 60 フォルダ地獄から抜けたあと、いちばんの変化は「技術自慢」ではなく、ファイル探し・競合解消・layout メンテに費やす時間が減った ことでした。

機能役割向く場面構文
Route Groups整理・layout 分離多エリア・チーム開発(folderName)
ネストレイアウトUI の段階追加多段ナビ各層 layout.js
Parallel Routes複数断片の同時表示ダッシュボード@folderName
Intercepting Routes遷移横取り・モーダルInstagram 風 UX(.) (..) (...)

最初から全部は不要。まず Route Groups で整理、多段 UI ならネストレイアウト、ダッシュボードやモーダルが必要になったら Parallel / Intercepting を足す、が現実的です。

最初は公式ドキュメントも難しく感じますが、一度組むと ディレクトリ=ルート、フォルダ深さ=UI 深さ、横取り=モーダル UX と腑に落ちます。

Next.js プロジェクトが散らかってきたら、半日だけ Route Groups リファクタを試してみてください。数か月後、過去の自分に感謝すると思います。

Next.js 高度なルーティングのリファクタリング完全フロー

混乱したディレクトリを Route Groups・ネストレイアウト・Parallel Routes・Intercepting Routes で再設計する

⏱️ 目安時間: 4 時間

  1. 1

    ステップ1: 既存プロジェクト構造を分析する

    現状のルート数と複雑さを評価:
    • app 配下のフォルダ数を数える(20 を超えたら Route Groups を検討)
    • 機能エリアを洗い出す(マーケティング、EC、管理画面など)
    • レイアウト要件が異なるページ群を特定
    • チーム協業での競合ポイントを記録

    判断基準:
    • フォルダ数 > 20:Route Groups
    • 多段ナビが必要:ネストレイアウト
    • 複数の独立モジュールを同時表示:Parallel Routes
    • モーダル UX が必要:Intercepting Routes + Parallel Routes
  2. 2

    ステップ2: Route Groups で機能エリアを分割する

    丸括弧で Route Groups を作成し、機能またはチーム単位で整理:
    • (marketing):トップ、About、価格など
    • (shop):商品、カートなど
    • (dashboard):注文、ユーザーなど

    注意:
    • グループ名は URL に出ない
    • 同じ URL を複数グループに置けない
    • 各グループに独自の layout.js を置ける
    • トップの page.js はいずれか 1 グループ内に置く(app/page.js 単独は不可の場合あり)
  3. 3

    ステップ3: ネストレイアウト階層を設計する

    UI 階層に合わせてネスト:
    • 第 1 層:app/layout.js(Header + Footer)
    • 第 2 層:blog/layout.js など(サイドバー)
    • 第 3 層:blog/[slug]/layout.js など(目次)

    ポイント:
    • 各 layout はその層固有の UI のみ追加
    • 子 layout は親を自動継承
    • 子ルート切り替え時に親 layout は再レンダリングされない
  4. 4

    ステップ4: Parallel Routes を実装する(必要な場合)

    @ でスロットを作成:
    • @modal:モーダル用
    • @sales、@orders:ダッシュボードの独立モジュール

    layout.js で受け取る:
    • export default function Layout({ children, modal, sales, orders })
    • JSX で {modal} {sales} {orders} を配置

    各スロットに loading.js と error.js を置ける
  5. 5

    ステップ5: Intercepting Routes を実装する(モーダルが必要な場合)

    横取り構造を作成:
    • @modal 下に (.)photos/[id]/page.js(同階層を横取り)
    • photos/[id]/page.js(フルページ)

    構文:
    • (.):同階層
    • (..):1 つ上
    • (...):app ルートから

    default.js で null を返し、モーダルを閉じられるようにする
  6. 6

    ステップ6: テストと検証

    確認項目:
    • Route Groups:URL 不変・構造が明確か
    • ネストレイアウト:UI 階層と状態保持
    • Parallel Routes:独立ロード・エラー分離
    • Intercepting Routes:クライアント遷移でモーダル、リロードでフルページ

    パフォーマンス:
    • Next.js DevTools で layout の再レンダリング回数
    • 子ルート切り替え時に親 layout が再マウントされないこと

FAQ

Route Groups は URL に影響しますか?
いいえ。`(marketing)` のように丸括弧で命名したフォルダ名は Next.js により URL から除外されます。例:`(marketing)/about/page.js` の URL は `/about` のままで、`/marketing/about` にはなりません。
いつ Route Groups を使うべきですか?
app 配下のフォルダが 20 を超える、または機能エリアごとに異なるルートレイアウトが必要なときです。複数チームで開発するプロジェクトでは Git 競合を大きく減らせます。
ネストレイアウトはパフォーマンスに影響しますか?
悪化しません。子ルート切り替え時、親の layout.js は再レンダリングされず、最内層の page.js のみ更新されます。サイドバーのスクロール位置や検索欄の入力など、layout 内の状態をページ遷移後も保持できます。
Parallel Routes と通常コンポーネントの違いは?
各スロットは独立したページフラグメントで、独自の loading.js・error.js を持ち、並列ロードされ互いに影響しません。通常コンポーネントは全データ待ちの単一ツリーになり、1 つ失敗するとページ全体に波及しやすいです。
Intercepting Routes の (.) (..) (...) はどう選ぶ?
(.) は同階層(@modal と photos が app 直下など)、(..) は 1 階層上、(...) は app ルートから横取りです。迷ったらまず (...) で動作確認し、構造に合わせて調整するのが安全です。
Intercepting Routes に default.js が必要な理由は?
ルートがスロットにマッチしないとき、default.js がないと前回のスロット内容(モーダル)が残ります。default.js で null を返すと、不一致時にスロットを空にでき、モーダルを正しく閉じられます。
Pages Router から App Router へ移行時、ルートを全面再設計すべき?
必須ではありません。小規模(フォルダ 20 未満)ならフラット構造のままでも構いません。大規模や複雑なレイアウト・モーダル UX がある場合は、Route Groups やネストレイアウトで再設計すると保守性が大きく上がります。

5分で読めます · 公開日: 2025年12月18日 · 更新日: 2026年6月8日

関連記事

コメント

GitHubアカウントでログインしてコメントできます