layout.js

El archivo layout se utiliza para definir un diseño en tu aplicación Next.js.

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

Un diseño raíz (root layout) es el diseño de nivel superior en el directorio raíz app. Se utiliza para definir las etiquetas <html> y <body> y otros elementos de interfaz de usuario compartidos globalmente.

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

Referencia

Props

children (requerido)

Los componentes de diseño deben aceptar y utilizar una prop children. Durante el renderizado, children se completará con los segmentos de ruta que el diseño está envolviendo. Estos serán principalmente el componente de un Layout hijo (si existe) o Page, pero también podrían ser otros archivos especiales como Loading o Error cuando corresponda.

params (opcional)

Una promesa que se resuelve en un objeto que contiene los parámetros de ruta dinámica desde el segmento raíz hasta ese diseño.

export default async function Layout({
  params,
}: {
  params: Promise<{ team: string }>
}) {
  const { team } = await params
}
Ruta de ejemploURLparams
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'] }>
  • Dado que la prop params es una promesa, debes usar async/await o la función use de React para acceder a los valores.
    • En la versión 14 y anteriores, params era una prop síncrona. Para mantener la compatibilidad con versiones anteriores, aún puedes acceder a ella de forma síncrona en Next.js 15, pero este comportamiento quedará obsoleto en el futuro.

Diseño raíz (Root Layout)

El directorio app debe incluir un app/layout.js raíz.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}
  • El diseño raíz debe definir las etiquetas <html> y <body>.
    • No debes agregar manualmente etiquetas <head> como <title> y <meta> en los diseños raíz. En su lugar, debes usar la API de Metadata que maneja automáticamente requisitos avanzados como streaming y desduplicación de elementos <head>.
  • Puedes usar grupos de rutas (route groups) para crear múltiples diseños raíz.
    • Navegar entre múltiples diseños raíz causará una recarga completa de página (en lugar de una navegación del lado del cliente). Por ejemplo, navegar desde /cart que usa app/(shop)/layout.js a /blog que usa app/(marketing)/layout.js causará una recarga completa de página. Esto solo aplica para múltiples diseños raíz.

Consideraciones

Objeto Request

Los diseños se almacenan en caché en el cliente durante la navegación para evitar solicitudes innecesarias al servidor.

Los diseños no se vuelven a renderizar. Pueden almacenarse en caché y reutilizarse para evitar cálculos innecesarios al navegar entre páginas. Al restringir el acceso al objeto request crudo en los diseños, Next.js puede evitar la ejecución de código de usuario potencialmente lento o costoso dentro del diseño, lo que podría afectar negativamente el rendimiento.

Para acceder al objeto request, puedes usar las APIs headers y cookies en Componentes del Servidor (Server Components) y Funciones.

import { cookies } from 'next/headers'

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

Parámetros de consulta (Query params)

Los diseños no se vuelven a renderizar en la navegación, por lo que no pueden acceder a los parámetros de búsqueda que de otro modo quedarían obsoletos.

Para acceder a los parámetros de consulta actualizados, puedes usar la prop searchParams de la Página, o leerlos dentro de un Componente del Cliente usando el hook useSearchParams. Dado que los Componentes del Cliente se vuelven a renderizar durante la navegación, tienen acceso a los últimos parámetros de consulta.

'use client'

import { useSearchParams } from 'next/navigation'

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

  const search = searchParams.get('search')

  return '...'
}

Pathname

Los diseños no se vuelven a renderizar en la navegación, por lo que no acceden al pathname que de otro modo quedaría obsoleto.

Para acceder al pathname actual, puedes leerlo dentro de un Componente del Cliente usando el hook usePathname. Dado que los Componentes del Cliente se vuelven a renderizar durante la navegación, tienen acceso al último pathname.

'use client'

import { usePathname } from 'next/navigation'

// Lógica simplificada 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>
  )
}

Obtención de datos (Fetching Data)

Los diseños no pueden pasar datos a sus children. Sin embargo, puedes obtener los mismos datos en una ruta más de una vez y usar cache de React para desduplicar las solicitudes sin afectar el rendimiento.

Alternativamente, al usar fetch en Next.js, las solicitudes se desduplican automáticamente.

export async function getUser(id: string) {
  const res = await fetch(`https://.../users/${id}`)
  return res.json()
}

Acceso a segmentos hijos

Los diseños no tienen acceso a los segmentos de ruta por debajo de sí mismos. Para acceder a todos los segmentos de ruta, puedes usar useSelectedLayoutSegment o useSelectedLayoutSegments en un Componente del Cliente.

'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}`}
      // Cambiar estilo dependiendo de si el enlace está activo
      style={{ fontWeight: isActive ? 'bold' : 'normal' }}
    >
      {children}
    </Link>
  )
}

Ejemplos

Metadata

Puedes modificar los elementos HTML <head> como title y meta usando el objeto metadata o la función generateMetadata.

import type { Metadata } from 'next'

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

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

Nota importante: No debes agregar manualmente etiquetas <head> como <title> y <meta> en los diseños raíz. En su lugar, usa las APIs de Metadata que manejan automáticamente requisitos avanzados como streaming y desduplicación de elementos <head>.

Enlaces de navegación activos

Puedes usar el hook usePathname para determinar si un enlace de navegación está activo.

Dado que usePathname es un hook del cliente, necesitas extraer los enlaces de navegación en un Componente del Cliente, que puede importarse en tu diseño:

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

      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        Acerca de
      </Link>
    </nav>
  )
}

Mostrar contenido basado en params

Utilizando segmentos de ruta dinámicos, puede mostrar o recuperar contenido específico basado en la propiedad params.

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

  return (
    <section>
      <header>
        <h1>Bienvenido al Panel de {team}</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}

Leer params en Componentes del Cliente (Client Components)

Para usar params en un Componente del Cliente (que no puede ser async), puede utilizar la función use de React para leer la promesa:

'use client'

import { use } from 'react'

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

Historial de Versiones

VersiónCambios
v15.0.0-RCparams ahora es una promesa. Hay disponible un codemod.
v13.0.0Se introdujo layout.