layout.js

Le fichier layout est utilisé pour définir une mise en page (layout) dans votre application Next.js.

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

Un layout racine est la mise en page la plus haute dans le répertoire racine app. Il est utilisé pour définir les balises <html> et <body> ainsi que d'autres éléments d'interface utilisateur partagés globalement.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

Référence

Props

children (requis)

Les composants de mise en page doivent accepter et utiliser une prop children. Pendant le rendu, children sera rempli avec les segments de route que le layout englobe. Il s'agira principalement du composant d'un Layout enfant (s'il existe) ou d'une Page, mais pourrait aussi être d'autres fichiers spéciaux comme Loading ou Error le cas échéant.

params (optionnel)

Une promesse qui se résout en un objet contenant les paramètres de route dynamique depuis le segment racine jusqu'à ce layout.

export default async function Layout({
  params,
}: {
  params: Promise<{ team: string }>
}) {
  const { team } = await params
}
export default async function Layout({ params }) {
  const { team } = await params
}
Exemple de routeURLparams
app/dashboard/[team]/layout.js/dashboard/1Promise<{ team: '1' }>
app/shop/[tag]/[item]/layout.js/shop/1/2Promise<{ tag: '1', item: '2' }>
app/blog/[...slug]/layout.js/blog/1/2Promise<{ slug: ['1', '2'] }>
  • Comme la prop params est une promesse, vous devez utiliser async/await ou la fonction use de React pour accéder aux valeurs.
    • Dans la version 14 et antérieures, params était une prop synchrone. Pour assurer la compatibilité ascendante, vous pouvez toujours y accéder de manière synchrone dans Next.js 15, mais ce comportement sera déprécié à l'avenir.

Layout racine

Le répertoire app doit inclure un app/layout.js racine.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}
export default function RootLayout({ children }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}
  • Le layout racine doit définir les balises <html> et <body>.
    • Vous ne devriez pas ajouter manuellement des balises <head> comme <title> et <meta> aux layouts racines. Utilisez plutôt l'API Metadata qui gère automatiquement des exigences avancées comme le streaming et la déduplication des éléments <head>.
  • Vous pouvez utiliser des groupes de routes pour créer plusieurs layouts racines.
    • Naviguer entre plusieurs layouts racines provoquera un rechargement complet de la page (par opposition à une navigation côté client). Par exemple, naviguer de /cart qui utilise app/(shop)/layout.js vers /blog qui utilise app/(marketing)/layout.js provoquera un rechargement complet. Cela s'applique uniquement aux multiples layouts racines.

Mises en garde

Objet Request

Les layouts sont mis en cache côté client pendant la navigation pour éviter des requêtes serveur inutiles.

Les Layouts ne sont pas rerendus. Ils peuvent être mis en cache et réutilisés pour éviter des calculs inutiles lors de la navigation entre pages. En restreignant l'accès des layouts à la requête brute, Next.js peut empêcher l'exécution de code utilisateur potentiellement lent ou coûteux dans le layout, ce qui pourrait nuire aux performances.

Pour accéder à l'objet request, vous pouvez utiliser les APIs headers et cookies dans les Composants Serveur et Fonctions.

import { cookies } from 'next/headers'

export default async function Layout({ children }) {
  const cookieStore = await cookies()
  const theme = cookieStore.get('theme')
  return '...'
}
import { cookies } from 'next/headers'

export default async function Layout({ children }) {
  const cookieStore = await cookies()
  const theme = cookieStore.get('theme')
  return '...'
}

Paramètres de requête

Les layouts ne sont pas rerendus lors de la navigation, donc ils ne peuvent pas accéder aux paramètres de recherche qui deviendraient obsolètes.

Pour accéder aux paramètres de requête mis à jour, vous pouvez utiliser la prop searchParams de la Page, ou les lire dans un Composant Client en utilisant le hook useSearchParams. Comme les Composants Clients sont rerendus lors de la navigation, ils ont accès aux derniers paramètres de requête.

'use client'

import { useSearchParams } from 'next/navigation'

export default function Search() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  return '...'
}
'use client'

import { useSearchParams } from 'next/navigation'

export default function Search() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  return '...'
}
import Search from '@/app/ui/search'

export default function Layout({ children }) {
  return (
    <>
      <Search />
      {children}
    </>
  )
}
import Search from '@/app/ui/search'

export default function Layout({ children }) {
  return (
    <>
      <Search />
      {children}
    </>
  )
}

Pathname

Les layouts ne sont pas rerendus lors de la navigation, donc ils n'ont pas accès au pathname qui deviendrait obsolète.

Pour accéder au pathname actuel, vous pouvez le lire dans un Composant Client en utilisant le hook usePathname. Comme les Composants Clients sont rerendus lors de la navigation, ils ont accès au dernier pathname.

'use client'

import { usePathname } from 'next/navigation'

// Logique simplifiée de breadcrumbs
export default function Breadcrumbs() {
  const pathname = usePathname()
  const segments = pathname.split('/')

  return (
    <nav>
      {segments.map((segment, index) => (
        <span key={index}>
          {' > '}
          {segment}
        </span>
      ))}
    </nav>
  )
}
'use client'

import { usePathname } from 'next/navigation'

// Logique simplifiée de breadcrumbs
export default function Breadcrumbs() {
  const pathname = usePathname()
  const segments = pathname.split('/')

  return (
    <nav>
      {segments.map((segment, index) => (
        <span key={index}>
          {' > '}
          {segment}
        </span>
      ))}
    </nav>
  )
}
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'

export default function Layout({ children }) {
  return (
    <>
      <Breadcrumbs />
      <main>{children}</main>
    </>
  )
}
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'

export default function Layout({ children }) {
  return (
    <>
      <Breadcrumbs />
      <main>{children}</main>
    </>
  )
}

Récupération de données

Les layouts ne peuvent pas transmettre de données à leurs children. Cependant, vous pouvez récupérer les mêmes données plusieurs fois dans une route, et utiliser le cache de React pour dédupliquer les requêtes sans affecter les performances.

Alternativement, lorsque vous utilisez fetch dans Next.js, les requêtes sont automatiquement dédupliquées.

export async function getUser(id: string) {
  const res = await fetch(`https://.../users/${id}`)
  return res.json()
}
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'

export default async function Layout({ children }) {
  const user = await getUser('1')

  return (
    <>
      <nav>
        {/* ... */}
        <UserName user={user.name} />
      </nav>
      {children}
    </>
  )
}
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'

export default async function Layout({ children }) {
  const user = await getUser('1')

  return (
    <>
      <nav>
        {/* ... */}
        <UserName user={user.name} />
      </nav>
      {children}
    </>
  )
}
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'

export default async function Page() {
  const user = await getUser('1')

  return (
    <div>
      <h1>Bienvenue {user.name}</h1>
    </div>
  )
}
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'

export default async function Page() {
  const user = await getUser('1')

  return (
    <div>
      <h1>Bienvenue {user.name}</h1>
    </div>
  )
}

Accès aux segments enfants

Les layouts n'ont pas accès aux segments de route en dessous d'eux-mêmes. Pour accéder à tous les segments de route, vous pouvez utiliser useSelectedLayoutSegment ou useSelectedLayoutSegments dans un Composant Client.

'use client'

import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'

export default function NavLink({
  slug,
  children,
}: {
  slug: string
  children: React.ReactNode
}) {
  const segment = useSelectedLayoutSegment()
  const isActive = slug === segment

  return (
    <Link
      href={`/blog/${slug}`}
      // Change le style selon que le lien est actif
      style={{ fontWeight: isActive ? 'bold' : 'normal' }}
    >
      {children}
    </Link>
  )
}
'use client'

import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'

export default function NavLinks({ slug, children }) {
  const segment = useSelectedLayoutSegment()
  const isActive = slug === segment

  return (
    <Link
      href={`/blog/${slug}`}
      style={{ fontWeight: isActive ? 'bold' : 'normal' }}
    >
      {children}
    </Link>
  )
}
import { NavLink } from './nav-link'
import getPosts from './get-posts'

export default async function Layout({
  children,
}: {
  children: React.ReactNode
}) {
  const featuredPosts = await getPosts()
  return (
    <div>
      {featuredPosts.map((post) => (
        <div key={post.id}>
          <NavLink slug={post.slug}>{post.title}</NavLink>
        </div>
      ))}
      <div>{children}</div>
    </div>
  )
}
import { NavLink } from './nav-link'
import getPosts from './get-posts'

export default async function Layout({ children }) {
  const featuredPosts = await getPosts()
  return (
    <div>
      {featuredPosts.map((post) => (
        <div key={post.id}>
          <NavLink slug={post.slug}>{post.title}</NavLink>
        </div>
      ))}
      <div>{children}</div>
    </div>
  )
}

Exemples

Métadonnées

Vous pouvez modifier les éléments HTML <head> comme title et meta en utilisant l'objet metadata ou la fonction generateMetadata.

import type { Metadata } from 'next'

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

export default function Layout({ children }: { children: React.ReactNode }) {
  return '...'
}
export const metadata = {
  title: 'Next.js',
}

export default function Layout({ children }) {
  return '...'
}

Bon à savoir : Vous ne devriez pas ajouter manuellement des balises <head> comme <title> et <meta> aux layouts racines. Utilisez plutôt les APIs Metadata qui gèrent automatiquement des exigences avancées comme le streaming et la déduplication des éléments <head>.

Liens de navigation actifs

Vous pouvez utiliser le hook usePathname pour déterminer si un lien de navigation est actif.

Comme usePathname est un hook client, vous devez extraire les liens de navigation dans un Composant Client, qui peut être importé dans votre layout :

'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function NavLinks() {
  const pathname = usePathname()

  return (
    <nav>
      <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
        Accueil
      </Link>

      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        À propos
      </Link>
    </nav>
  )
}
'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function Links() {
  const pathname = usePathname()

  return (
    <nav>
      <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
        Accueil
      </Link>

      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        À propos
      </Link>
    </nav>
  )
}
import { NavLinks } from '@/app/ui/nav-links'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <NavLinks />
        <main>{children}</main>
      </body>
    </html>
  )
}
import { NavLinks } from '@/app/ui/nav-links'

export default function Layout({ children }) {
  return (
    <html lang="en">
      <body>
        <NavLinks />
        <main>{children}</main>
      </body>
    </html>
  )
}

Affichage de contenu basé sur les params

En utilisant les segments de route dynamiques, vous pouvez afficher ou récupérer du contenu spécifique en fonction de la prop params.

export default async function DashboardLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ team: string }>
}) {
  const { team } = await params

  return (
    <section>
      <header>
        <h1>Bienvenue sur le tableau de bord de {team}</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}
export default async function DashboardLayout({ children, params }) {
  const { team } = await params

  return (
    <section>
      <header>
        <h1>Bienvenue sur le tableau de bord de {team}</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}

Lecture des params dans les composants clients

Pour utiliser params dans un composant client (qui ne peut pas être async), vous pouvez utiliser la fonction use de React pour lire la promesse :

'use client'

import { use } from 'react'

export default function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = use(params)
}
'use client'

import { use } from 'react'

export default function Page({ params }) {
  const { slug } = use(params)
}

Historique des versions

VersionModifications
v15.0.0-RCparams est désormais une promesse. Un codemod est disponible.
v13.0.0Introduction de layout.