Busca en todas las páginas de la documentación
Los Layouts envuelven páginas con UI compartida que persiste entre navegaciones. Nunca se remontan — usa plantillas cuando necesites estado fresco.
Tarjeta de referencia rápida — lista para copiar y pegar.
// app/layout.tsx — Root layout (requerido, exactamente uno)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es">
<body>
<header>Encabezado del sitio</header>
<main>{children}</main>
<footer>Pie de página del sitio</footer>
</body>
</html>
);
}
// app/dashboard/layout.tsx — Nested layout para /dashboard/*
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex">
<nav className="w-64">Barra lateral</nav>
<section className="flex-1">{children}</section>
</div>
);
}Cuándo usarlo: Siempre que múltiples páginas compartan la misma UI envolvente — barras de navegación, barras laterales, proveedores o contenedores estructurales.
// app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
// app/dashboard/layout.tsx
import Link from "next/link";
const sidebarLinks = [
{ href: "/dashboard", label: "Resumen" },
{ href: "/dashboard/analytics", label: "Analítica" },
{ href: "/dashboard/settings", label: "Configuración" },
];
export
// app/dashboard/page.tsx
export default function DashboardPage() {
return <h1>Resumen del Panel de control</h1>;
}
// app/dashboard/analytics/page.tsx
export default function AnalyticsPage() {
return <h1>Analítica</h1>;
// Navegar aquí mantiene la barra lateral montada — sin remount, sin pérdida de estado
<html> y <body>. Next.js genera un error si falta este archivo.children. El prop children es la página actual o el siguiente layout anidado en el árbol.app/layout.tsx envuelve app/dashboard/layout.tsx que envuelve app/dashboard/page.tsx. Nunca los compones manualmente."use client" solo cuando el layout en sí necesita hooks o APIs del navegador.metadata o función generateMetadata que se fusiona con los metadatos del padre.// Template — se remontan en cada navegación (útil para animaciones de entrada/salida)
// app/dashboard/template.tsx
"use client";
import { motion } from "framer-motion";
export default function DashboardTemplate({ children }: { children: React.ReactNode }) {
return (
<
// Layout con params — accediendo a params de ruta en un layout
// app/blog/[slug]/layout.tsx
export default async function BlogPostLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ slug: string }>;
// Layout condicional — UI diferente según estado de autenticación
// app/dashboard/layout.tsx
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode
// Tipo de props del layout
interface LayoutProps {
children: React.ReactNode;
params: Promise<Record<string, string>>; // async en Next.js 15+
}
// Template tiene los mismos props que layout
interface TemplateProps {
children:
children. Usa React Context, cookies o una búsqueda de datos compartida en su lugar.<html> y <body>. Incluso como Client Component, debe renderizar esas etiquetas.layout.tsx > template.tsx > page.tsx. La plantilla se sitúa entre ellos.searchParams. Solo page.tsx recibe searchParams. Si un layout necesita params de consulta, usa useSearchParams() en un hijo Client Component.app/dashboard/layout.tsx, todas las páginas del panel de control pierden ese envolvente al instante.params son asíncronos en Next.js 15+. Siempre await params en layouts — el patrón de desestructuración síncrona está deprecado.| Enfoque | Cuándo Usar |
|---|---|
template.tsx | Necesitas remount de componente en cada navegación (animaciones, logging) |
| Route Groups con layouts separados | Diferentes layouts para diferentes secciones en la misma profundidad de URL |
| Proveedor de contexto del lado del cliente | Compartir estado entre páginas sin un envolvente visual |
Parallel Routes (@slot) | Renderizar múltiples páginas lado a lado en un layout |
De una aplicación SaaS de producción con Next.js 15 / React 19 (SystemsArchitect.io).
// Ejemplo de producción: Root layout con proveedores y metadatos
// Archivo: app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import { ThemeProvider } from '@/components/theme-provider';
import { ToastProvider } from '@/components/toast-provider';
import { ProjectStoreInitializer } from '@/components/project-store-initializer'
// Ejemplo de producción: Página de inicio con datos del servidor
// Archivo: app/page.tsx
import { getFeaturedContent } from '@/lib/content';
export default async function HomePage() {
const featured = await getFeaturedContent();
return (
<main>
<h1>Bienvenido a SystemsArchitect</
Lo que esto demuestra en producción:
ThemeProvider envuelve todo porque las notificaciones toast y el inicializador de almacén ambos necesitan contexto de tema. ToastProvider viene antes de ProjectStoreInitializer porque los errores de inicialización del almacén necesitan mostrar mensajes toast.suppressHydrationWarning en la etiqueta <html> es requerido cuando se usan proveedores de tema que establecen un atributo class o data-theme en el servidor. Sin él, la desincronización de hidratación entre renderizado del servidor y del cliente desencadena una advertencia de React.ThemeProvider, ToastProvider e ProjectStoreInitializer se inicializan una vez y persisten en todas las transiciones de página. Cualquier estado en estos proveedores se conserva.metadata.title.template ('%s | SystemsArchitect') permite a las páginas secundarias establecer solo su título (p. ej., export const metadata = { title: 'Panel de control' }) y se renderiza automáticamente como "Panel de control | SystemsArchitect".getServerSideProps del Pages Router.El root app/layout.tsx debe existir y debe contener las etiquetas <html> y <body>. Next.js genera un error si falta este archivo porque define la estructura del documento HTML para toda la aplicación.
No. Los Layouts persisten entre navegaciones. React los reconcilia para que el estado de componentes, efectos y DOM se conserven. Por eso las barras laterales y barras de navegación permanecen interactivas.
layout.tsx persiste y no se remontas en la navegacióntemplate.tsx se remontas en cada navegación, dando estado frescolayout.tsx > template.tsx > page.tsxNo. Los Layouts solo reciben children como prop. Usa React Context, cookies o una búsqueda de datos compartida para compartir datos entre un layout y sus páginas secundarias.
No. Solo page.tsx recibe searchParams. Si un layout necesita params de consulta, usa useSearchParams() en un hijo Client Component.
// app/blog/[slug]/layout.tsx
export default async function BlogLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ slug: string }>;
Establece title: { default: "Mi Aplicación", template: "%s | Mi Aplicación" } en el root layout. Las páginas secundarias exportan metadata = { title: "Panel de control" } y se renderiza como "Panel de control | Mi Aplicación".
Se rompe. params ahora es una Promise en Next.js 15+. Debes await params en layouts. El patrón síncrono está deprecado.
interface LayoutProps {
children: React.ReactNode;
params: Promise<Record<string, string>>;
}
interface TemplateProps {
children: React.ReactNode
import type { Metadata, ResolvingMetadata } from "next";
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> },
parent: ResolvingMetadata
):
Sí, pero aún debe renderizar las etiquetas <html> y <body>. Incluso como Client Component, estas etiquetas son requeridas.
app/layout.tsx envuelve app/dashboard/layout.tsx que envuelve app/dashboard/page.tsx. Nunca los compones manualmente. Next.js maneja el anidamiento según la jerarquía del sistema de archivos.