layout.js
El archivo layout
se utiliza para definir un diseño en su aplicación Next.js.
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
return <section>{children}</section>
}
Un diseño raíz (root layout) es el diseño 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>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Referencia
Props
children
(requerido)
Los componentes de diseño deben aceptar y usar 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
}
export default async function Layout({ params }) {
const { team } = await params
}
Ruta de ejemplo | URL | params |
---|---|---|
app/dashboard/[team]/layout.js | /dashboard/1 | Promise<{ team: '1' }> |
app/shop/[tag]/[item]/layout.js | /shop/1/2 | Promise<{ tag: '1', item: '2' }> |
app/blog/[...slug]/layout.js | /blog/1/2 | Promise<{ slug: ['1', '2'] }> |
- Dado que la prop
params
es una promesa, debe usarasync/await
o la funciónuse
de React para acceder a los valores.- En la versión 14 y anteriores,
params
era una prop síncrona. Para ayudar con la compatibilidad hacia atrás, aún puede acceder a ella de forma síncrona en Next.js 15, pero este comportamiento quedará obsoleto en el futuro.
- En la versión 14 y anteriores,
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>
)
}
export default function RootLayout({ children }) {
return (
<html>
<body>{children}</body>
</html>
)
}
- El diseño raíz debe definir las etiquetas
<html>
y<body>
.- No debe agregar manualmente etiquetas
<head>
como<title>
y<meta>
a los diseños raíz. En su lugar, debe usar la API de Metadata que maneja automáticamente requisitos avanzados como la transmisión en flujo (streaming) y la desduplicación de elementos<head>
.
- No debe agregar manualmente etiquetas
- Puede 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 la página (en lugar de una navegación del lado del cliente). Por ejemplo, navegar desde
/cart
que usaapp/(shop)/layout.js
a/blog
que usaapp/(marketing)/layout.js
causará una recarga completa de la página. Esto solo aplica para múltiples diseños raíz.
- Navegar entre múltiples diseños raíz causará una recarga completa de la página (en lugar de una navegación del lado del cliente). Por ejemplo, navegar desde
Advertencias
Objeto de solicitud (Request Object)
Los diseños se almacenan en caché en el cliente durante la navegación para evitar solicitudes innecesarias al servidor.
Los diseños (Layouts) 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 de los diseños a la solicitud cruda, 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 de solicitud, puede 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 '...'
}
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, puede 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 '...'
}
'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
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, puede 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.
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>
</>
)
}
Obtención de datos (Fetching Data)
Los diseños no pueden pasar datos a sus children
. Sin embargo, puede 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, cuando se usa 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()
}
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>
)
}
Acceso a segmentos hijos
Los diseños no tienen acceso a los segmentos de ruta debajo de sí mismos. Para acceder a todos los segmentos de ruta, puede usar useSelectedLayoutSegment
o useSelectedLayoutSegments
en un Componente del Cliente.
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>
)
}
Ejemplos
Metadata
Puede 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 '...'
}
export const metadata = {
title: 'Next.js',
}
export default function Layout({ children }) {
return '...'
}
Nota importante: No debe agregar manualmente etiquetas
<head>
como<title>
y<meta>
a los diseños raíz. En su lugar, use las APIs de Metadata que manejan automáticamente requisitos avanzados como la transmisión en flujo (streaming) y la desduplicación de elementos<head>
.
Enlaces de navegación activos
Puede usar el hook usePathname
para determinar si un enlace de navegación está activo.
Dado que usePathname
es un hook del cliente, necesita extraer los enlaces de navegación en un Componente del Cliente, que puede importar en su diseño:
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>
)
}
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>
)
}
export default async function DashboardLayout({ children, params }) {
const { team } = await params
return (
<section>
<header>
<h1>Bienvenido al Panel de {team}</h1>
</header>
<main>{children}</main>
</section>
)
}
Leer params
en Componentes Cliente
Para usar params
en un Componente 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)
}
'use client'
import { use } from 'react'
export default function Page({ params }) {
const { slug } = use(params)
}
Historial de Versiones
Versión | Cambios |
---|---|
v15.0.0-RC | params ahora es una promesa. Hay disponible un codemod. |
v13.0.0 | Se introdujo layout . |