Parallele Routen

Parallele Routen ermöglichen es Ihnen, eine oder mehrere Seiten gleichzeitig oder bedingt innerhalb desselben Layouts zu rendern. Sie sind nützlich für hochdynamische Bereiche einer App, wie Dashboards und Feeds in sozialen Netzwerken.

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

Diagramm zu parallelen Routen

Konvention

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:

Dateisystemstruktur für parallele Routen

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

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 die URL-Struktur nicht. Für /@analytics/views lautet die URL beispielsweise /views, da @analytics ein Slot ist. Slots werden mit der regulären Page-Komponente kombiniert, um die endgültige Seite zu bilden, die mit dem Routensegment verknüpft ist. Daher können Sie auf derselben Ebene des Routensegments keine separaten statischen und dynamischen Slots haben. Wenn ein Slot dynamisch ist, müssen alle Slots auf dieser Ebene dynamisch sein.

Gut zu wissen:

  • Die children-Prop ist ein impliziter Slot, der keinem Ordner zugeordnet werden muss. Das bedeutet, dass app/page.js äquivalent zu app/@children/page.js ist.

default.js

Sie können eine default.js-Datei definieren, die als Fallback für nicht übereinstimmende Slots während des initialen Ladens oder eines vollständigen Seiten-Neuladens gerendert wird.

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

Nicht übereinstimmende Routen in parallelen Routen

Bei der Navigation zu /settings rendert der @team-Slot die /settings-Seite, während die aktuell aktive Seite für den @analytics-Slot beibehalten wird.

Beim Neuladen rendert Next.js eine default.js für @analytics. Wenn default.js nicht existiert, wird stattdessen eine 404 gerendert.

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

Verhalten

Standardmäßig behält Next.js den aktiven Zustand (oder die Unterseite) für jeden Slot im Auge. Der in einem Slot gerenderte Inhalt hängt jedoch von der Art der Navigation ab:

  • Soft Navigation: Bei der clientseitigen Navigation führt Next.js ein partielles Rendering durch, ändert die Unterseite innerhalb des Slots und behält die aktiven Unterseiten der anderen Slots bei, auch wenn sie nicht mit der aktuellen URL übereinstimmen.
  • Hard Navigation: Nach einem vollständigen Seitenladen (Browser-Neuladen) kann Next.js den aktiven Zustand für die Slots, die nicht mit der aktuellen URL übereinstimmen, nicht bestimmen. Stattdessen wird eine default.js-Datei für die nicht übereinstimmenden Slots gerendert oder 404, wenn default.js nicht existiert.

Gut zu wissen:

  • Die 404 für nicht übereinstimmende Routen stellt sicher, dass Sie nicht versehentlich eine parallele Route auf einer Seite rendern, für die sie nicht vorgesehen war.

Beispiele

Mit useSelectedLayoutSegment(s)

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

'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, ist loginSegment gleich dem String "login".

Bedingte Routen

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

Diagramm zu bedingten Routen
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, um Benutzern zu ermöglichen, den Slot unabhängig zu navigieren. Dies ist nützlich für die Erstellung von Tabs.

Beispielsweise hat der @analytics-Slot 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 beiden Seiten zu teilen:

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>
    </>
  )
}

Modale

Parallele Routen können zusammen mit Intercepting Routes verwendet werden, um Modale mit Deep Linking zu erstellen. Dies ermöglicht es Ihnen, häufige Herausforderungen beim Erstellen von Modalen zu lösen, wie z.B.:

  • Teilbarkeit des Modal-Inhalts über eine URL.
  • Beibehaltung des Kontexts beim Neuladen der Seite, anstatt das Modal zu schließen.
  • Schließen des Modals bei Rückwärtsnavigation anstatt zur vorherigen Route zu gehen.
  • Wiederöffnen des Modals bei Vorwärtsnavigation.

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

Diagramm zu parallelen Routen mit Auth-Modal

Um dieses Muster zu implementieren, erstellen Sie zunächst eine /login-Route, die Ihre Haupt-Login-Seite rendert.

Diagramm zu parallelen Routen mit Login-Seite
import { Login } from '@/app/ui/login'

export default function Page() {
  return <Login />
}

Fügen Sie dann im @auth-Slot eine default.js-Datei hinzu, die null zurückgibt. Dadurch wird sichergestellt, dass das Modal nicht gerendert wird, wenn es nicht aktiv ist.

export default function Default() {
  return null
}

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

import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'

export default function Page() {
  return (
    <Modal>
      <Login />
    </Modal>
  )
}

Gut zu wissen:

  • Die zur Interception der Route verwendete Konvention, z.B. (.), hängt von Ihrer Dateisystemstruktur ab. Siehe Intercepting Routes-Konvention.
  • Durch die Trennung der <Modal>-Funktionalität vom Modal-Inhalt (<Login>) können Sie sicherstellen, dass jeglicher Inhalt innerhalb des Modals, z.B. Formulare, Server-Komponenten sind. Siehe Interleaving Client and Server Components für weitere Informationen.

Öffnen des Modals

Nun können Sie den Next.js-Router nutzen, um das Modal zu öffnen und zu schließen. Dadurch wird sichergestellt, dass die URL korrekt aktualisiert wird, wenn das Modal geöffnet ist, und bei Vorwärts- und Rückwärtsnavigation.

Um das Modal zu öffnen, übergeben Sie den @auth-Slot als Prop an das übergeordnete Layout und rendern ihn zusammen mit der children-Prop.

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>
    </>
  )
}

Wenn der Benutzer auf den <Link> klickt, öffnet sich das Modal anstatt zur /login-Seite zu navigieren. Beim Neuladen oder initialen Laden führt die Navigation zu /login jedoch zur Haupt-Login-Seite.

Schließen des Modals

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

'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>
    </>
  )
}

Wenn Sie die Link-Komponente verwenden, um von einer Seite wegzunavigieren, die den @auth-Slot nicht mehr rendern sollte, müssen Sie sicherstellen, dass die parallele Route auf eine Komponente abgebildet wird, die null zurückgibt. Zum Beispiel erstellen wir beim Navigieren zurück zur Root-Seite eine @auth/page.tsx-Komponente:

import Link from 'next/link'

export function Modal({ children }: { children: React.ReactNode }) {
  return (
    <>
      <Link href="/">Close modal</Link>
      <div>{children}</div>
    </>
  )
}

Oder wenn Sie zu einer anderen Seite navigieren (z.B. /foo, /foo/bar, etc.), können Sie einen Catch-all-Slot verwenden:

export default function CatchAll() {
  return null
}

Gut zu wissen:

  • Wir verwenden eine Catch-all-Route in unserem @auth-Slot, um das Modal zu schließen, aufgrund des Verhaltens von parallelen Routen(#behavior). Da clientseitige Navigationen zu einer Route, die nicht mehr mit dem Slot übereinstimmt, sichtbar bleiben, müssen wir den Slot auf eine Route abbilden, die null zurückgibt, um das Modal zu schließen.
  • Weitere Beispiele könnten das Öffnen eines Foto-Modals in einer Galerie sein, während es auch eine dedizierte /photo/[id]-Seite gibt, oder das Öffnen eines Warenkorbs in einem seitlichen Modal.
  • Sehen Sie sich ein Beispiel für Modale mit Intercepted und Parallel Routes an.

Lade- und Fehler-UI

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

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

Weitere Informationen finden Sie in der Dokumentation zu Loading UI und Error Handling.

On this page