Routes parallèles

Les Routes parallèles vous permettent d'afficher simultanément ou conditionnellement une ou plusieurs pages dans la même mise en page. Elles sont utiles pour les sections hautement dynamiques d'une application, comme les tableaux de bord et les flux sur les réseaux sociaux.

Par exemple, pour un tableau de bord, vous pouvez utiliser des routes parallèles pour afficher simultanément les pages team et analytics :

Diagramme des Routes parallèles

Convention

Emplacements (Slots)

Les routes parallèles sont créées en utilisant des emplacements nommés. Les emplacements sont définis avec la convention @dossier. Par exemple, la structure de fichiers suivante définit deux emplacements : @analytics et @team :

Structure de fichiers des Routes parallèles

Les emplacements sont passés comme props à la mise en page parente partagée. Pour l'exemple ci-dessus, le composant dans app/layout.js accepte maintenant les props des emplacements @analytics et @team, et peut les afficher en parallèle avec la prop children :

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

Cependant, les emplacements ne sont pas des segments de route et n'affectent pas la structure de l'URL. Par exemple, pour /@analytics/views, l'URL sera /views car @analytics est un emplacement. Les emplacements sont combinés avec le composant Page régulier pour former la page finale associée au segment de route. Pour cette raison, vous ne pouvez pas avoir des emplacements statiques et dynamiques séparés au même niveau de segment de route. Si un emplacement est dynamique, tous les emplacements à ce niveau doivent être dynamiques.

Bon à savoir :

  • La prop children est un emplacement implicite qui n'a pas besoin d'être mappé à un dossier. Cela signifie que app/page.js est équivalent à app/@children/page.js.

default.js

Vous pouvez définir un fichier default.js à afficher comme solution de repli pour les emplacements non correspondants lors du chargement initial ou du rechargement complet de la page.

Considérez la structure de dossiers suivante. L'emplacement @team a une page /settings, mais @analytics n'en a pas.

Routes parallèles avec routes non correspondantes

Lors de la navigation vers /settings, l'emplacement @team affichera la page /settings tout en conservant la page actuellement active pour l'emplacement @analytics.

Lors d'un rechargement, Next.js affichera un default.js pour @analytics. Si default.js n'existe pas, une 404 est affichée à la place.

De plus, comme children est un emplacement implicite, vous devez également créer un fichier default.js pour afficher une solution de repli pour children lorsque Next.js ne peut pas récupérer l'état actif de la page parente.

Comportement

Par défaut, Next.js garde une trace de l'état actif (ou sous-page) pour chaque emplacement. Cependant, le contenu affiché dans un emplacement dépendra du type de navigation :

  • Navigation douce : Lors de la navigation côté client, Next.js effectuera un rendu partiel, changeant la sous-page dans l'emplacement, tout en conservant les sous-pages actives des autres emplacements, même si elles ne correspondent pas à l'URL actuelle.
  • Navigation dure : Après un chargement complet de la page (rechargement du navigateur), Next.js ne peut pas déterminer l'état actif pour les emplacements qui ne correspondent pas à l'URL actuelle. À la place, il affichera un fichier default.js pour les emplacements non correspondants, ou 404 si default.js n'existe pas.

Bon à savoir :

  • La 404 pour les routes non correspondantes aide à s'assurer que vous n'affichez pas accidentellement une route parallèle sur une page pour laquelle elle n'était pas prévue.

Exemples

Avec useSelectedLayoutSegment(s)

useSelectedLayoutSegment et useSelectedLayoutSegments acceptent tous deux un paramètre parallelRoutesKey, qui vous permet de lire le segment de route actif dans un emplacement.

'use client'

import { useSelectedLayoutSegment } from 'next/navigation'

export default function Layout({ auth }: { auth: React.ReactNode }) {
  const loginSegment = useSelectedLayoutSegment('auth')
  // ...
}

Lorsqu'un utilisateur navigue vers app/@auth/login (ou /login dans la barre d'URL), loginSegment sera égal à la chaîne "login".

Routes conditionnelles

Vous pouvez utiliser les Routes parallèles pour afficher conditionnellement des routes en fonction de certaines conditions, comme le rôle de l'utilisateur. Par exemple, pour afficher une page de tableau de bord différente pour les rôles /admin ou /user :

Diagramme des routes conditionnelles
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
}

Groupes d'onglets

Vous pouvez ajouter un layout dans un emplacement pour permettre aux utilisateurs de naviguer dans l'emplacement indépendamment. Ceci est utile pour créer des onglets.

Par exemple, l'emplacement @analytics a deux sous-pages : /page-views et /visitors.

Emplacement analytics avec deux sous-pages et un layout

Dans @analytics, créez un fichier layout pour partager les onglets entre les deux pages :

import Link from 'next/link'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <nav>
        <Link href="/page-views">Vues de page</Link>
        <Link href="/visitors">Visiteurs</Link>
      </nav>
      <div>{children}</div>
    </>
  )
}

Modales

Les Routes parallèles peuvent être utilisées avec les Routes d'interception pour créer des modales qui prennent en charge le lien profond. Cela vous permet de résoudre les défis courants lors de la création de modales, tels que :

  • Rendre le contenu de la modale partageable via une URL.
  • Préserver le contexte lorsque la page est rechargée, au lieu de fermer la modale.
  • Fermer la modale lors d'une navigation arrière plutôt que d'aller à la route précédente.
  • Rouvrir la modale lors d'une navigation avant.

Considérez le modèle d'interface utilisateur suivant, où un utilisateur peut ouvrir une modale de connexion à partir d'une mise en page en utilisant la navigation côté client, ou accéder à une page /login séparée :

Diagramme des Routes parallèles

Pour implémenter ce modèle, commencez par créer une route /login qui affiche votre page de connexion principale.

Diagramme des Routes parallèles
import { Login } from '@/app/ui/login'

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

Ensuite, dans l'emplacement @auth, ajoutez un fichier default.js qui retourne null. Cela garantit que la modale n'est pas affichée lorsqu'elle n'est pas active.

export default function Default() {
  return null
}

Dans votre emplacement @auth, interceptez la route /login en mettant à jour le dossier /(.)login. Importez le composant <Modal> et ses enfants dans le fichier /(.)login/page.tsx :

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

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

Bon à savoir :

Ouverture de la modale

Maintenant, vous pouvez exploiter le routeur Next.js pour ouvrir et fermer la modale. Cela garantit que l'URL est correctement mise à jour lorsque la modale est ouverte, et lors des navigations avant et arrière.

Pour ouvrir la modale, passez l'emplacement @auth comme prop à la mise en page parente et affichez-le avec la prop children.

import Link from 'next/link'

export default function Layout({
  auth,
  children,
}: {
  auth: React.ReactNode
  children: React.ReactNode
}) {
  return (
    <>
      <nav>
        <Link href="/login">Ouvrir la modale</Link>
      </nav>
      <div>{auth}</div>
      <div>{children}</div>
    </>
  )
}

Lorsque l'utilisateur clique sur le <Link>, la modale s'ouvrira au lieu de naviguer vers la page /login. Cependant, lors d'un rechargement ou d'un chargement initial, la navigation vers /login amènera l'utilisateur à la page de connexion principale.

Fermeture de la modale

Vous pouvez fermer la modale en appelant router.back() ou en utilisant le composant Link.

'use client'

import { useRouter } from 'next/navigation'

export function Modal({ children }: { children: React.ReactNode }) {
  const router = useRouter()

  return (
    <>
      <button
        onClick={() => {
          router.back()
        }}
      >
        Fermer la modale
      </button>
      <div>{children}</div>
    </>
  )
}

Lorsque vous utilisez le composant Link pour naviguer loin d'une page qui ne devrait plus afficher l'emplacement @auth, nous devons nous assurer que la route parallèle correspond à un composant qui retourne null. Par exemple, lors de la navigation vers la page racine, nous créons un composant @auth/page.tsx :

import Link from 'next/link'

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

Ou si vous naviguez vers une autre page (comme /foo, /foo/bar, etc.), vous pouvez utiliser un emplacement catch-all :

export default function CatchAll() {
  return null
}

Bon à savoir :

  • Nous utilisons une route catch-all dans notre emplacement @auth pour fermer la modale en raison du comportement des routes parallèles(#behavior). Comme les navigations côté client vers une route qui ne correspond plus à l'emplacement resteront visibles, nous devons faire correspondre l'emplacement à une route qui retourne null pour fermer la modale.
  • D'autres exemples pourraient inclure l'ouverture d'une modale photo dans une galerie tout en ayant une page dédiée /photo/[id], ou l'ouverture d'un panier d'achat dans une modale latérale.
  • Voir un exemple de modales avec Routes interceptées et parallèles.

UI de chargement et d'erreur

Les Routes parallèles peuvent être diffusées indépendamment, vous permettant de définir des états d'erreur et de chargement indépendants pour chaque route :

Les routes parallèles permettent des états d'erreur et de chargement personnalisés

Voir la documentation UI de chargement et Gestion des erreurs pour plus d'informations.

On this page