Pages et Layouts

Nous recommandons de lire les pages Fondamentaux du routage et Définir des routes avant de continuer.

Les fichiers spéciaux layout.js, page.js et template.js vous permettent de créer une interface utilisateur pour une route. Cette page vous guidera sur comment et quand utiliser ces fichiers spéciaux.

Pages

Une page est une interface utilisateur unique à une route. Vous pouvez définir une page en exportant par défaut un composant depuis un fichier page.js.

Par exemple, pour créer votre page index, ajoutez le fichier page.js dans le répertoire app :

Fichier spécial page.js
// `app/page.tsx` est l'interface utilisateur pour l'URL `/`
export default function Page() {
  return <h1>Bonjour, page d'accueil !</h1>
}
// `app/page.js` est l'interface utilisateur pour l'URL `/`
export default function Page() {
  return <h1>Bonjour, page d'accueil !</h1>
}

Ensuite, pour créer d'autres pages, créez un nouveau dossier et ajoutez-y le fichier page.js. Par exemple, pour créer une page pour la route /dashboard, créez un nouveau dossier appelé dashboard et ajoutez-y le fichier page.js :

// `app/dashboard/page.tsx` est l'interface utilisateur pour l'URL `/dashboard`
export default function Page() {
  return <h1>Bonjour, page Dashboard !</h1>
}
// `app/dashboard/page.js` est l'interface utilisateur pour l'URL `/dashboard`
export default function Page() {
  return <h1>Bonjour, page Dashboard !</h1>
}

Bon à savoir :

  • Les extensions de fichier .js, .jsx ou .tsx peuvent être utilisées pour les Pages.
  • Une page est toujours la feuille du sous-arbre de route.
  • Un fichier page.js est requis pour rendre un segment de route accessible publiquement.
  • Les pages sont par défaut des Composants Serveur, mais peuvent être configurées comme Composants Client.
  • Les pages peuvent récupérer des données. Consultez la section Récupération de données pour plus d'informations.

Layouts

Un layout est une interface utilisateur partagée entre plusieurs routes. Lors de la navigation, les layouts préservent l'état, restent interactifs et ne se rechargent pas. Les layouts peuvent également être imbriqués.

Vous pouvez définir un layout en exportant par défaut un composant React depuis un fichier layout.js. Le composant doit accepter une prop children qui sera remplie avec un layout enfant (s'il existe) ou une page lors du rendu.

Par exemple, le layout sera partagé avec les pages /dashboard et /dashboard/settings :

Fichier spécial layout.js
export default function DashboardLayout({
  children, // sera une page ou un layout imbriqué
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* Incluez ici une interface partagée, par exemple un en-tête ou une barre latérale */}
      <nav></nav>

      {children}
    </section>
  )
}
export default function DashboardLayout({
  children, // sera une page ou un layout imbriqué
}) {
  return (
    <section>
      {/* Incluez ici une interface partagée, par exemple un en-tête ou une barre latérale */}
      <nav></nav>

      {children}
    </section>
  )
}

Layout racine (obligatoire)

Le layout racine est défini au niveau supérieur du répertoire app et s'applique à toutes les routes. Ce layout est obligatoire et doit contenir les balises html et body, vous permettant de modifier le HTML initial renvoyé par le serveur.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="fr">
      <body>
        {/* Interface du layout */}
        <main>{children}</main>
      </body>
    </html>
  )
}
export default function RootLayout({ children }) {
  return (
    <html lang="fr">
      <body>
        {/* Interface du layout */}
        <main>{children}</main>
      </body>
    </html>
  )
}

Imbrication des Layouts

Par défaut, les layouts dans la hiérarchie des dossiers sont imbriqués, ce qui signifie qu'ils enveloppent les layouts enfants via leur prop children. Vous pouvez imbriquer des layouts en ajoutant layout.js dans des segments de route spécifiques (dossiers).

Par exemple, pour créer un layout pour la route /dashboard, ajoutez un nouveau fichier layout.js dans le dossier dashboard :

Layout imbriqué
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
  return <section>{children}</section>
}

Si vous combinez les deux layouts ci-dessus, le layout racine (app/layout.js) enveloppera le layout dashboard (app/dashboard/layout.js), qui enveloppera les segments de route dans app/dashboard/*.

Les deux layouts seraient imbriqués comme suit :

Layouts imbriqués

Bon à savoir :

  • Les extensions de fichier .js, .jsx ou .tsx peuvent être utilisées pour les Layouts.
  • Seul le layout racine peut contenir les balises <html> et <body>.
  • Lorsqu'un fichier layout.js et page.js sont définis dans le même dossier, le layout enveloppera la page.
  • Les layouts sont par défaut des Composants Serveur mais peuvent être configurés comme Composants Client.
  • Les layouts peuvent récupérer des données. Consultez la section Récupération de données pour plus d'informations.
  • Il n'est pas possible de passer des données entre un layout parent et ses enfants. Cependant, vous pouvez récupérer les mêmes données plusieurs fois dans une route, et React dédupliquera automatiquement les requêtes sans affecter les performances.
  • Les layouts n'ont pas accès aux segments de route en dessous d'eux. Pour accéder à tous les segments de route, vous pouvez utiliser useSelectedLayoutSegment ou useSelectedLayoutSegments dans un Composant Client.
  • Vous pouvez utiliser des Groupes de routes pour inclure ou exclure des segments de route spécifiques des layouts partagés.
  • Vous pouvez utiliser des Groupes de routes pour créer plusieurs layouts racine. Voir un exemple ici.
  • Migration depuis le répertoire pages : Le layout racine remplace les fichiers _app.js et _document.js. Consultez le guide de migration.

Templates

Les templates sont similaires aux layouts en ce qu'ils enveloppent chaque layout enfant ou page. Contrairement aux layouts qui persistent entre les routes et maintiennent l'état, les templates créent une nouvelle instance pour chacun de leurs enfants lors de la navigation. Cela signifie que lorsqu'un utilisateur navigue entre des routes partageant un template, une nouvelle instance du composant est montée, les éléments DOM sont recréés, l'état n'est pas préservé et les effets sont resynchronisés.

Il peut y avoir des cas où vous avez besoin de ces comportements spécifiques, et les templates seraient une option plus appropriée que les layouts. Par exemple :

  • Fonctionnalités reposant sur useEffect (par exemple, journalisation des vues de page) et useState (par exemple, un formulaire de feedback par page).
  • Pour modifier le comportement par défaut du framework. Par exemple, les Suspense Boundaries dans les layouts ne montrent le fallback que la première fois que le Layout est chargé et non lors du changement de page. Pour les templates, le fallback est affiché à chaque navigation.

Un template peut être défini en exportant par défaut un composant React depuis un fichier template.js. Le composant doit accepter une prop children.

Fichier spécial template.js
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}
export default function Template({ children }) {
  return <div>{children}</div>
}

En termes d'imbrication, template.js est rendu entre un layout et ses enfants. Voici un résultat simplifié :

Résultat
<Layout>
  {/* Notez que le template a une clé unique. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

Métadonnées

Dans le répertoire app, vous pouvez modifier les éléments HTML <head> tels que title et meta en utilisant les API de métadonnées.

Les métadonnées peuvent être définies en exportant un objet metadata ou une fonction generateMetadata dans un fichier layout.js ou page.js.

import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}
export const metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}

Bon à savoir : Vous ne devriez pas ajouter manuellement des balises <head> comme <title> et <meta> aux layouts racine. Utilisez plutôt l'API de métadonnées qui gère automatiquement des besoins avancés comme le streaming et la déduplication des éléments <head>.

Apprenez-en plus sur les options de métadonnées disponibles dans la référence API.