コンテンツへスキップ

並列ルーティング

並列ルーティングを使用すると、同じレイアウト内で1つ以上のページを同時に、または条件付きでレンダリングできます。これらは、ダッシュボードやソーシャルサイトのフィードなど、アプリの非常に動的なセクションに役立ちます。

たとえば、ダッシュボードを考えると、並列ルーティングを使用してteamページとanalyticsページを同時にレンダリングできます。

Parallel Routes Diagram

スロット

並列ルートは、名前付きの**スロット**を使用して作成されます。スロットは@folder規約で定義されます。たとえば、以下のファイル構造では、@analytics@teamの2つのスロットが定義されています。

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と同じです。

アクティブ状態とナビゲーション

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

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

ご存じの通り:

  • 一致しないルートに対する404は、意図しないページに並列ルートが誤ってレンダリングされるのを防ぐのに役立ちます。

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ファイルを作成する必要があります。

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スロットには/page-views/visitorsという2つのサブページがあります。

Analytics slot with two subpages and a layout

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

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経由で共有可能にする**。
  • モーダルを閉じる代わりに、ページを更新しても**コンテキストを保持する**。
  • 前のルートに戻るのではなく、**戻るナビゲーションでモーダルを閉じる**。
  • 進むナビゲーションでモーダルを再度開く.

以下のUIパターンを考えてみましょう。ユーザーはクライアントサイドナビゲーションを使用してレイアウトからログインモーダルを開くことも、個別の/loginページにアクセスすることもできます。

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フォルダを更新して/loginルートをインターセプトします。<Modal>コンポーネントとその子を/(.)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>
    </>
  )
}

Linkコンポーネントを使用して、もはや@authスロットをレンダリングすべきではないページから移動する場合、並列ルートが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エラーハンドリングのドキュメントを参照してください。