コンテンツにスキップ

並列ルート

パラレルルートを使用すると、同じレイアウト内で複数のページを同時に、または条件付きでレンダリングできます。これは、ダッシュボードやソーシャルサイトのフィードなど、アプリの高度に動的なセクションに役立ちます。

たとえば、ダッシュボードを考慮すると、パラレルルートを使用して teamanalytics のページを同時にレンダリングできます。

Parallel Routes Diagram

規約

スロット

パラレルルートは、名前付きのスロットを使用して作成されます。スロットは @folder という規約で定義されます。たとえば、次のファイル構造は 2 つのスロット: @analytics@team を定義します。

Parallel Routes File-system Structure

スロットは、親レイアウトにプロパティとして渡されます。上記の例では、app/layout.js のコンポーネントは @analytics@team のスロットプロパティを受け入れ、それらを children プロパティと並行してレンダリングできます。

app/layout.tsx
export default function Layout({
  children,
  team,
  analytics,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {children}
      {team}
      {analytics}
    </>
  )
}

ただし、スロットはルートセグメントではなく、URL構造には影響しません。たとえば、/@analytics/views の場合、@analytics はスロットであるため、URL は /views になります。スロットは、通常の Page コンポーネントと組み合わせて、ルートセグメントに関連付けられた最終的なページを形成します。このため、同じルートセグメントレベルで 静的 スロットと 動的 スロットを個別に持つことはできません。いずれか 1 つのスロットが動的な場合、そのレベルのすべてスロットは動的である必要があります。

知っておくと良いこと:

  • children プロパティは、フォルダにマッピングする必要のない暗黙的なスロットです。これは、app/page.jsapp/@children/page.js と同等であることを意味します。

default.js

default.js ファイルを定義して、初回ロードまたはフルページリロード中に、一致しないスロットのフォールバックとしてレンダリングできます。

次のフォルダ構造を検討してください。@team スロットには /settings ページがありますが、@analytics にはありません。

Parallel Routes unmatched routes

/settings に移動すると、@team スロットは /settings ページをレンダリングし、@analytics スロットの現在アクティブなページを維持します。

リフレッシュすると、Next.js は @analyticsdefault.js をレンダリングします。default.js が存在しない場合は、404 がレンダリングされます。

さらに、children は暗黙的なスロットであるため、Next.js が親ページの有効な状態を回復できない場合に children のフォールバックをレンダリングするために default.js ファイルを作成する必要があります。

動作

デフォルトでは、Next.js は各スロットの有効な*状態*(またはサブページ)を追跡します。ただし、スロット内でレンダリングされるコンテンツは、ナビゲーションの種類によって異なります。

  • ソフトナビゲーション: クライアントサイドナビゲーション中、Next.js は 部分的なレンダリング を実行し、スロット内のサブページを変更しながら、URL と一致しない場合でも、他のスロットの有効なサブページを維持します。
  • ハードナビゲーション: フルページロード(ブラウザのリフレッシュ)後、Next.js は URL と一致しないスロットの有効な状態を判断できません。代わりに、一致しないスロットの default.js ファイルをレンダリングするか、default.js が存在しない場合は 404 をレンダリングします。

知っておくと良いこと:

  • 一致しないルートの 404 は、意図しないページにパラレルルートを誤ってレンダリングしないようにするのに役立ちます。

useSelectedLayoutSegment(s) を使用

useSelectedLayoutSegmentuseSelectedLayoutSegments の両方に parallelRoutesKey パラメータがあり、スロット内のアクティブなルートセグメントを読み取ることができます。

app/layout.tsx
'use client'
 
import { useSelectedLayoutSegment } from 'next/navigation'
 
export default function Layout({ auth }: { auth: React.ReactNode }) {
  const loginSegment = useSelectedLayoutSegment('auth')
  // ...
}

ユーザーが app/@auth/login (または URL バーの /login)に移動すると、loginSegment は文字列 "login" と等しくなります。

条件付きルート

パラレルルートを使用して、ユーザーロールなどの特定の条件に基づいてルートを条件付きでレンダリングできます。たとえば、/admin または /user ロールに対して異なるダッシュボードページをレンダリングする場合。

Conditional routes diagram
app/dashboard/layout.tsx
import { checkUserRole } from '@/lib/auth'
 
export default function Layout({
  user,
  admin,
}: {
  user: React.ReactNode
  admin: React.ReactNode
}) {
  const role = checkUserRole()
  return role === 'admin' ? admin : user
}

タブグループ

スロット内に layout を追加することで、ユーザーがスロットを独立してナビゲートできるようになります。これはタブの作成に役立ちます。

たとえば、@analytics スロットには 2 つのサブページがあります: /page-views/visitors

Analytics slot with two subpages and a layout

@analytics 内で、layout ファイルを作成して、2 つのページ間でタブを共有します。

app/@analytics/layout.tsx
import Link from 'next/link'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <nav>
        <Link href="/page-views">Page Views</Link>
        <Link href="/visitors">Visitors</Link>
      </nav>
      <div>{children}</div>
    </>
  )
}

モーダル

パラレルルートは、インターセプトルートと組み合わせて、ディープリンクをサポートするモーダルを作成できます。これにより、モーダルの構築時に一般的な課題を解決できます。

  • モーダルコンテンツを**URL経由で共有可能**にする。
  • ページがリフレッシュされたときに、モーダルが閉じるのではなく、**コンテキストを維持**する。
  • 後方ナビゲーション時に、前のルートに移動するのではなく、**モーダルを閉じる**。
  • 前方ナビゲーション時にモーダルを再度開く.

ユーザーがクライアントサイドナビゲーションを使用してレイアウトからログインモーダルを開くか、個別の /login ページにアクセスできる UI パターンを検討してください。

Parallel Routes Diagram

このパターンを実装するには、まず**メイン**ログインページをレンダリングする /login ルートを作成します。

Parallel Routes Diagram
app/login/page.tsx
import { Login } from '@/app/ui/login'
 
export default function Page() {
  return <Login />
}

次に、@auth スロット内に、null を返す default.js ファイルを追加します。これにより、アクティブでないときにモーダルがレンダリングされないようになります。

app/@auth/default.tsx
export default function Default() {
  return null
}

@auth スロット内で、/login ルートをインターセプトするには、<Modal> コンポーネントとその子を @auth/(.)login/page.tsx ファイルにインポートし、フォルダ名を /@auth/(.)login/page.tsx に更新します。

app/@auth/(.)login/page.tsx
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
 
export default function Page() {
  return (
    <Modal>
      <Login />
    </Modal>
  )
}

知っておくと良いこと

モーダルを開く

これで、Next.js ルーターを活用してモーダルを開閉できます。これにより、モーダルが開いているときに URL が正しく更新され、後方および前方にナビゲートするときにも同様に更新されます。

モーダルを開くには、@auth スロットを親レイアウトにプロパティとして渡し、children プロパティと一緒にレンダリングします。

app/layout.tsx
import Link from 'next/link'
 
export default function Layout({
  auth,
  children,
}: {
  auth: React.ReactNode
  children: React.ReactNode
}) {
  return (
    <>
      <nav>
        <Link href="/login">Open modal</Link>
      </nav>
      <div>{auth}</div>
      <div>{children}</div>
    </>
  )
}

ユーザーが <Link> をクリックすると、/login ページにナビゲートする代わりにモーダルが開きます。ただし、リフレッシュまたは初回ロード時に /login にナビゲートすると、ユーザーはメインのログインページに移動します。

モーダルを閉じる

router.back() を呼び出すか、Link コンポーネントを使用することでモーダルを閉じることができます。

app/ui/modal.tsx
'use client'
 
import { useRouter } from 'next/navigation'
 
export function Modal({ children }: { children: React.ReactNode }) {
  const router = useRouter()
 
  return (
    <>
      <button
        onClick={() => {
          router.back()
        }}
      >
        Close modal
      </button>
      <div>{children}</div>
    </>
  )
}

@auth スロットをレンダリングする必要がなくなったページにナビゲートするために Link コンポーネントを使用する場合、パラレルルートが null を返すコンポーネントに一致することを確認する必要があります。たとえば、ルートページに戻るときは、@auth/page.tsx コンポーネントを作成します。

app/ui/modal.tsx
import Link from 'next/link'
 
export function Modal({ children }: { children: React.ReactNode }) {
  return (
    <>
      <Link href="/">Close modal</Link>
      <div>{children}</div>
    </>
  )
}
app/@auth/page.tsx
export default function Page() {
  return null
}

または、他のページ(/foo/foo/bar など)にナビゲートする場合は、キャッチオールスロットを使用できます。

app/@auth/[...catchAll]/page.tsx
export default function CatchAll() {
  return null
}

知っておくと良いこと

  • パラレルルートの動作により、@auth スロットでキャッチオールルートを使用します。スロットに一致しなくなったルートへのクライアントサイドナビゲーションは表示されたままになるため、モーダルを閉じるために null を返すルートにスロットを一致させる必要があります。
  • その他の例としては、ギャラリーで写真モーダルを開きながら、専用の /photo/[id] ページを持つこと、またはサイドモーダルでショッピングカートを開くことなどが挙げられます。
  • インターセプトおよびパラレルルートを使用したモーダルの例を表示する

ローディングとエラーUI

パラレルルートは独立してストリームできるため、各ルートの独立したエラーおよびローディング状態を定義できます。

Parallel routes enable custom error and loading states

詳細については、ローディングUI および エラーハンドリング のドキュメントを参照してください。