layout.js

Die layout-Datei wird verwendet, um ein Layout in Ihrer Next.js-Anwendung zu definieren.

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

Ein Root Layout ist das oberste Layout im Stammverzeichnis app. Es wird verwendet, um die <html>- und <body>-Tags sowie andere global geteilte UI-Elemente zu definieren.

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

Referenz

Props

children (erforderlich)

Layout-Komponenten sollten eine children-Prop akzeptieren und verwenden. Während des Renderings wird children mit den Routensegmenten gefüllt, die das Layout umschließt. Dies sind in erster Linie die Komponenten eines untergeordneten Layouts (falls vorhanden) oder einer Seite, aber auch andere spezielle Dateien wie Loading oder Error, falls zutreffend.

params (optional)

Ein Promise, das sich zu einem Objekt auflöst, das die dynamischen Routenparameter vom Root-Segment bis zu diesem Layout enthält.

export default async function Layout({
  params,
}: {
  params: Promise<{ team: string }>
}) {
  const { team } = await params
}
export default async function Layout({ params }) {
  const { team } = await params
}
Beispiel-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'] }>
  • Da die params-Prop ein Promise ist, müssen Sie async/await oder Reacts use-Funktion verwenden, um auf die Werte zuzugreifen.
    • In Version 14 und früher war params eine synchrone Prop. Zur Abwärtskompatibilität können Sie in Next.js 15 weiterhin synchron darauf zugreifen, aber dieses Verhalten wird in Zukunft veraltet sein.

Root Layout

Das app-Verzeichnis muss ein Root-app/layout.js enthalten.

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>
  )
}
  • Das Root-Layout muss <html>- und <body>-Tags definieren.
    • Sie sollten nicht manuell <head>-Tags wie <title> und <meta> zu Root-Layouts hinzufügen. Verwenden Sie stattdessen die Metadata API, die erweiterte Anforderungen wie Streaming und Deduplizierung von <head>-Elementen automatisch handhabt.
  • Sie können Route Groups verwenden, um mehrere Root-Layouts zu erstellen.
    • Die Navigation zwischen mehreren Root-Layouts verursacht einen vollständigen Seitenneuladevorgang (im Gegensatz zu einer clientseitigen Navigation). Beispielsweise verursacht die Navigation von /cart, das app/(shop)/layout.js verwendet, zu /blog, das app/(marketing)/layout.js verwendet, einen vollständigen Seitenneuladevorgang. Dies gilt nur für mehrere Root-Layouts.

Einschränkungen

Request-Objekt

Layouts werden während der Navigation im Client zwischengespeichert, um unnötige Server-Anfragen zu vermeiden.

Layouts werden nicht neu gerendert. Sie können zwischengespeichert und wiederverwendet werden, um unnötige Berechnungen bei der Navigation zwischen Seiten zu vermeiden. Durch die Beschränkung des Zugriffs auf den rohen Request in Layouts kann Next.js die Ausführung von potenziell langsamen oder teuren Benutzercode im Layout verhindern, was die Leistung negativ beeinflussen könnte.

Um auf das Request-Objekt zuzugreifen, können Sie die headers- und cookies-APIs in Server Components und Functions verwenden.

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 '...'
}

Query-Parameter

Layouts werden bei der Navigation nicht neu gerendert, daher können sie nicht auf Suchparameter zugreifen, die sonst veraltet wären.

Um auf aktualisierte Query-Parameter zuzugreifen, können Sie die searchParams-Prop der Seite verwenden oder sie in einer Client-Komponente mit dem useSearchParams-Hook lesen. Da Client-Komponenten bei der Navigation neu gerendert werden, haben sie Zugriff auf die neuesten Query-Parameter.

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

Pfadname

Layouts werden bei der Navigation nicht neu gerendert, daher können sie nicht auf den Pfadnamen zugreifen, der sonst veraltet wäre.

Um auf den aktuellen Pfadnamen zuzugreifen, können Sie ihn in einer Client-Komponente mit dem usePathname-Hook lesen. Da Client-Komponenten bei der Navigation neu gerendert werden, haben sie Zugriff auf den neuesten Pfadnamen.

'use client'

import { usePathname } from 'next/navigation'

// Vereinfachte Breadcrumb-Logik
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'

// Vereinfachte Breadcrumb-Logik
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>
    </>
  )
}

Datenabruf

Layouts können keine Daten an ihre children übergeben. Sie können jedoch dieselben Daten in einer Route mehrmals abrufen und Reacts cache verwenden, um die Anfragen ohne Leistungseinbußen zu deduplizieren.

Alternativ werden Anfragen bei Verwendung von fetch in Next.js automatisch dedupliziert.

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>Welcome {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>Welcome {user.name}</h1>
    </div>
  )
}

Zugriff auf untergeordnete Segmente

Layouts haben keinen Zugriff auf die Routensegmente unterhalb ihrer selbst. Um auf alle Routensegmente zuzugreifen, können Sie useSelectedLayoutSegment oder useSelectedLayoutSegments in einer Client-Komponente verwenden.

'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}`}
      // Ändere den Stil basierend darauf, ob der Link aktiv ist
      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>
  )
}

Beispiele

Metadaten

Sie können die <head>-HTML-Elemente wie title und meta mit dem metadata-Objekt oder der generateMetadata-Funktion ändern.

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 '...'
}

Gut zu wissen: Sie sollten nicht manuell <head>-Tags wie <title> und <meta> zu Root-Layouts hinzufügen. Verwenden Sie stattdessen die Metadata APIs, die erweiterte Anforderungen wie Streaming und Deduplizierung von <head>-Elementen automatisch handhaben.

Sie können den usePathname-Hook verwenden, um festzustellen, ob ein Navigationslink aktiv ist.

Da usePathname ein Client-Hook ist, müssen Sie die Navigationslinks in eine Client-Komponente auslagern, die in Ihr Layout importiert werden kann:

'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="/">
        Home
      </Link>

      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        Über uns
      </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="/">
        Home
      </Link>

      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        Über uns
      </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>
  )
}

Anzeigen von Inhalten basierend auf params

Mithilfe von dynamischen Routensegmenten können Sie spezifische Inhalte anzeigen oder abrufen, die auf der params-Prop basieren.

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

  return (
    <section>
      <header>
        <h1>Willkommen im Dashboard von {team}</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}
export default async function DashboardLayout({ children, params }) {
  const { team } = await params

  return (
    <section>
      <header>
        <h1>Willkommen im Dashboard von {team}</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}

Lesen von params in Client Components

Um params in einer Client Component (die nicht async sein kann) zu verwenden, können Sie die use-Funktion von React nutzen, um das Promise zu lesen:

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

Versionsverlauf

VersionÄnderungen
v15.0.0-RCparams ist jetzt ein Promise. Ein Codemod ist verfügbar.
v13.0.0layout eingeführt.