Menu

Parallele Routen

Parallele Routen ermöglichen das gleichzeitige oder bedingte Rendern von einer oder mehreren Seiten innerhalb desselben Layouts. Sie sind nützlich für hochdynamische Abschnitte einer App, wie Dashboards und Feeds in sozialen Netzwerken.

Zum Beispiel können Sie bei einem Dashboard parallele Routen verwenden, um die Seiten team und analytics gleichzeitig zu rendern:

Diagramm paralleler Routen

Slots

Parallele Routen werden mit benannten Slots erstellt. Slots werden mit der @folder-Konvention definiert. Die folgende Dateistruktur definiert beispielsweise zwei Slots: @analytics und @team:

Dateistruktursystem für parallele Routen

Slots werden als Props an das gemeinsame übergeordnete Layout übergeben. Für das obige Beispiel akzeptiert die Komponente in app/layout.js nun die Props @analytics und @team und kann sie parallel neben der children-Prop rendern:

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

Slots sind jedoch keine Routensegmente und beeinflussen nicht die URL-Struktur. Beispielsweise wird bei /@analytics/views die URL /views sein, da @analytics ein Slot ist. Slots werden mit der regulären Page-Komponente kombiniert, um die finale Seite des Routensegments zu bilden. Daher können Sie keine separaten statischen und dynamischen Slots auf derselben Routensegmentebene haben. Wenn ein Slot dynamisch ist, müssen alle Slots auf dieser Ebene dynamisch sein.

Hinweis:

  • Die children-Prop ist ein impliziter Slot, der nicht auf einen Ordner abgebildet werden muss. Das bedeutet, app/page.js entspricht app/@children/page.js.

Aktiver Zustand und Navigation

Standardmäßig verfolgt Next.js den aktiven Zustand (oder die Unterseite) für jeden Slot. Der Inhalt, der innerhalb eines Slots gerendert wird, hängt jedoch von der Art der Navigation ab:

  • Weiche Navigation: Bei der Navigation auf Clientseite führt Next.js ein partielles Rendering durch und ändert die Unterseite innerhalb des Slots, während die aktiven Unterseiten der anderen Slots beibehalten werden, auch wenn sie nicht mit der aktuellen URL übereinstimmen.
  • Harte Navigation: Nach einem vollständigen Seitenaufruf (Browser-Aktualisierung) kann Next.js den aktiven Zustand für die Slots, die nicht mit der aktuellen URL übereinstimmen, nicht ermitteln. Stattdessen wird eine default.js-Datei für die nicht übereinstimmenden Slots gerendert oder 404, wenn default.js nicht existiert.

Hinweis:

  • Der 404-Status für nicht übereinstimmende Routen hilft sicherzustellen, dass eine parallele Route nicht versehentlich auf einer Seite gerendert wird, für die sie nicht vorgesehen war.

default.js

Sie können eine default.js-Datei definieren, um beim erstmaligen Laden oder bei einer vollständigen Seitenaktualisierung einen Fallback für nicht übereinstimmende Slots zu rendern.

Betrachten Sie die folgende Ordnerstruktur. Der @team-Slot hat eine /settings-Seite, aber @analytics nicht.

Parallele Routen mit nicht übereinstimmenden Routen

Bei der Navigation zu /settings wird der @team-Slot die /settings-Seite rendern und dabei die aktuell aktive Seite für den @analytics-Slot beibehalten.

Bei einer Aktualisierung rendert Next.js eine default.js für @analytics. Wenn default.js nicht existiert, wird ein 404 gerendert.

Da children ein impliziter Slot ist, müssen Sie auch eine default.js-Datei erstellen, um einen Fallback für children zu rendern, wenn Next.js den aktiven Zustand der übergeordneten Seite nicht wiederherstellen kann.

useSelectedLayoutSegment(s)

Sowohl useSelectedLayoutSegment als auch useSelectedLayoutSegments akzeptieren einen parallelRoutesKey-Parameter, mit dem Sie das aktive Routensegment innerhalb eines Slots lesen können.

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

Wenn ein Benutzer zu app/@auth/login (oder /login in der URL-Leiste) navigiert, wird loginSegment der Zeichenfolge "login" entsprechen.

Beispiele

Bedingte Routen

Sie können parallele Routen verwenden, um Routen basierend auf bestimmten Bedingungen zu rendern, z. B. Benutzerrolle. Um beispielsweise eine andere Dashboard-Seite für die Rollen /admin oder /user zu rendern:

Diagramm bedingter Routen
app/dashboard/layout.tsx
TypeScript
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}</>
}

Tab-Gruppen

Sie können ein layout innerhalb eines Slots hinzufügen, damit Benutzer unabhängig durch den Slot navigieren können. Dies ist nützlich zum Erstellen von Tabs.

Der @analytics-Slot hat beispielsweise zwei Unterseiten: /page-views und /visitors.

Analytics-Slot mit zwei Unterseiten und einem Layout

Erstellen Sie innerhalb von @analytics eine layout-Datei, um die Tabs zwischen den zwei Seiten zu teilen:

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

Modals

Parallele Routen können zusammen mit Intercepting Routes verwendet werden, um Modals mit Deep-Linking zu erstellen. Dies ermöglicht es, häufige Herausforderungen bei der Entwicklung von Modals zu lösen, wie z. B.:

  • Das modale Fenster-Inhalt über eine URL teilbar machen.
  • Kontext beibehalten, wenn die Seite aktualisiert wird, anstatt das modale Fenster zu schließen.
  • Das modale Fenster bei Rückwärtsnavigation schließen anstatt zur vorherigen Route zu wechseln.
  • Das modale Fenster bei Vorwärtsnavigation wieder öffnen.

Betrachten Sie das folgende UI-Muster, bei dem ein Benutzer ein Login-Modal aus einem Layout mittels clientseitiger Navigation öffnen oder eine separate /login-Seite aufrufen kann:

Parallele Routen Diagramm

Um dieses Muster zu implementieren, beginnen Sie mit dem Erstellen einer /login-Route, die Ihre Haupt-Login-Seite rendert.

Parallele Routen Diagramm
app/login/page.tsx
TypeScript
import { Login } from '@/app/ui/login'
 
export default function Page() {
  return <Login />
}

Fügen Sie dann in den @auth-Slot eine default.js-Datei hinzu, die null zurückgibt. Dies stellt sicher, dass das modale Fenster nicht gerendert wird, wenn es nicht aktiv ist.

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

Intercepten Sie in Ihrem @auth-Slot die /login-Route, indem Sie den /(.)login-Ordner aktualisieren. Importieren Sie die <Modal>-Komponente und deren Kinder in die /(.)login/page.tsx-Datei:

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

Hinweis:

Nun können Sie den Next.js-Router nutzen, um das modale Fenster zu öffnen und zu schließen. Dies stellt sicher, dass die URL korrekt aktualisiert wird, wenn das modale Fenster geöffnet und bei Vor- und Zurücknavigation geändert wird.

Um das modale Fenster zu öffnen, übergeben Sie den @auth-Slot als Prop an das übergeordnete Layout und rendern Sie ihn neben dem children-Prop.

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

Wenn der Benutzer auf den <Link> klickt, wird das modale Fenster geöffnet, anstatt zur /login-Seite zu navigieren. Bei Aktualisierung oder Erstladen führt die Navigation zu /login den Benutzer jedoch zur Haupt-Login-Seite.

Sie können das modale Fenster schließen, indem Sie router.back() aufrufen oder die Link-Komponente verwenden.

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

Bei Verwendung der Link-Komponente zum Navigieren von einer Seite, die den @auth-Slot nicht mehr rendern sollte, müssen wir sicherstellen, dass die parallele Route mit einer Komponente übereinstimmt, die null zurückgibt. Wenn wir beispielsweise zur Startseite zurücknavigieren, erstellen wir eine @auth/page.tsx-Komponente:

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

Oder bei Navigation zu einer anderen Seite (wie /foo, /foo/bar usw.) können Sie einen Catch-All-Slot verwenden:

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

Hinweis:

  • Wir verwenden eine Catch-All-Route in unserem @auth-Slot, um das modale Fenster aufgrund des in Aktiver Zustand und Navigation beschriebenen Verhaltens zu schließen. Da clientseitige Navigationen zu einer Route, die den Slot nicht mehr übereinstimmt, weiterhin sichtbar bleiben, müssen wir den Slot mit einer Route abgleichen, die null zurückgibt, um das modale Fenster zu schließen.
  • Andere Beispiele könnten das Öffnen eines Foto-Modals in einer Galerie bei gleichzeitiger Existenz einer dedizierten /photo/[id]-Seite oder das Öffnen eines Warenkorbs in einem Seitenmodal sein.
  • Sehen Sie ein Beispiel für Modals mit Intercepted und Parallelen Routen.

Lade- und Fehler-UI

Parallele Routen können unabhängig gestreamt werden, sodass Sie für jede Route unabhängige Fehler- und Ladeustände definieren können:

Parallele Routen ermöglichen benutzerdefinierte Fehler- und Ladeustände

Weitere Informationen finden Sie in der Lade-UI- und Fehlerbehandlungs-Dokumentation.