Páginas y Layouts

Recomendamos leer las páginas Fundamentos de Enrutamiento y Definiendo Rutas antes de continuar.

El App Router en Next.js 13 introdujo nuevas convenciones de archivos para crear fácilmente páginas, layouts compartidos y plantillas. Esta guía te mostrará cómo usar estos archivos especiales en tu aplicación Next.js.

Páginas

Una página es una interfaz de usuario única para una ruta. Puedes definir páginas exportando un componente desde un archivo page.js. Usa carpetas anidadas para definir una ruta y un archivo page.js para hacer la ruta accesible públicamente.

Crea tu primera página añadiendo un archivo page.js dentro del directorio app:

Archivo especial page.js
// `app/page.tsx` es la UI para la URL `/`
export default function Page() {
  return <h1>¡Hola, página de inicio!</h1>
}
// `app/page.js` es la UI para la URL `/`
export default function Page() {
  return <h1>¡Hola, página de inicio!</h1>
}
// `app/dashboard/page.tsx` es la UI para la URL `/dashboard`
export default function Page() {
  return <h1>¡Hola, página de Dashboard!</h1>
}
// `app/dashboard/page.js` es la UI para la URL `/dashboard`
export default function Page() {
  return <h1>¡Hola, página de Dashboard!</h1>
}

Es bueno saber:

Layouts

Un layout es una interfaz de usuario compartida entre múltiples páginas. En la navegación, los layouts preservan el estado, mantienen la interactividad y no se vuelven a renderizar. Los layouts también pueden estar anidados.

Puedes definir un layout exportando por defecto un componente React desde un archivo layout.js. El componente debe aceptar una prop children que se llenará con un layout hijo (si existe) o una página hija durante el renderizado.

Archivo especial layout.js
export default function DashboardLayout({
  children, // será una página o layout anidado
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* Incluye UI compartida aquí, ej. un encabezado o barra lateral */}
      <nav></nav>

      {children}
    </section>
  )
}
export default function DashboardLayout({
  children, // será una página o layout anidado
}) {
  return (
    <section>
      {/* Incluye UI compartida aquí, ej. un encabezado o barra lateral */}
      <nav></nav>

      {children}
    </section>
  )
}

Es bueno saber:

  • El layout superior se llama Root Layout (Layout Raíz). Este layout requerido es compartido por todas las páginas en una aplicación. Los layouts raíz deben contener etiquetas html y body.
  • Cualquier segmento de ruta puede opcionalmente definir su propio Layout. Estos layouts serán compartidos por todas las páginas en ese segmento.
  • Los layouts en una ruta están anidados por defecto. Cada layout padre envuelve los layouts hijos debajo de él usando la prop children de React.
  • Puedes usar Grupos de Rutas (Route Groups) para incluir o excluir segmentos de ruta específicos de layouts compartidos.
  • Los layouts son Componentes del Servidor (Server Components) por defecto pero pueden configurarse como Componentes del Cliente (Client Components).
  • Los layouts pueden obtener datos. Consulta la sección Obtención de Datos (Data Fetching) para más información.
  • No es posible pasar datos entre un layout padre y sus hijos. Sin embargo, puedes obtener los mismos datos en una ruta múltiples veces, y React deduplicará automáticamente las solicitudes sin afectar el rendimiento.
  • Los layouts no tienen acceso a los segmentos de ruta debajo de sí mismos. Para acceder a todos los segmentos de ruta, puedes usar useSelectedLayoutSegment o useSelectedLayoutSegments en un Componente del Cliente.
  • Se pueden usar extensiones de archivo .js, .jsx o .tsx para Layouts.
  • Un archivo layout.js y page.js pueden definirse en la misma carpeta. El layout envolverá la página.

Root Layout (Obligatorio)

El layout raíz se define en el nivel superior del directorio app y aplica a todas las rutas. Este layout te permite modificar el HTML inicial devuelto desde el servidor.

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

Es bueno saber:

Migrando desde el directorio pages: El layout raíz reemplaza los archivos _app.js y _document.js. Consulta la guía de migración.

Anidando Layouts

Los layouts definidos dentro de una carpeta (ej. app/dashboard/layout.js) aplican a segmentos de ruta específicos (ej. acme.com/dashboard) y se renderizan cuando esos segmentos están activos. Por defecto, los layouts en la jerarquía de archivos están anidados, lo que significa que envuelven layouts hijos a través de su prop children.

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

Es bueno saber:

  • Solo el layout raíz puede contener las etiquetas <html> y <body>.

Si combinaras los dos layouts anteriores, el layout raíz (app/layout.js) envolvería el layout del dashboard (app/dashboard/layout.js), que a su vez envolvería los segmentos de ruta dentro de app/dashboard/*.

Los dos layouts estarían anidados así:

Layouts Anidados

Puedes usar Grupos de Rutas (Route Groups) para incluir o excluir segmentos de ruta específicos de layouts compartidos.

Plantillas

Las plantillas son similares a los layouts en que envuelven cada layout hijo o página. A diferencia de los layouts que persisten entre rutas y mantienen el estado, las plantillas crean una nueva instancia para cada uno de sus hijos en la navegación. Esto significa que cuando un usuario navega entre rutas que comparten una plantilla, se monta una nueva instancia del componente, se recrean los elementos DOM, el estado no se preserva y los efectos se resincronizan.

Puede haber casos donde necesites esos comportamientos específicos, y las plantillas serían una opción más adecuada que los layouts. Por ejemplo:

  • Características que dependen de useEffect (ej. registro de visitas de página) y useState (ej. un formulario de retroalimentación por página).
  • Para cambiar el comportamiento predeterminado del framework. Por ejemplo, los Límites de Suspense (Suspense Boundaries) dentro de layouts solo muestran el fallback la primera vez que se carga el Layout y no al cambiar páginas. Para plantillas, el fallback se muestra en cada navegación.

Una plantilla puede definirse exportando por defecto un componente React desde un archivo template.js. El componente debe aceptar una prop children.

Archivo especial template.js
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}
export default function Template({ children }) {
  return <div>{children}</div>
}

En términos de anidación, template.js se renderiza entre un layout y sus hijos. Aquí hay una salida simplificada:

Salida
<Layout>
  {/* Nota que la plantilla recibe una clave única. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

Modificando <head>

En el directorio app, puedes modificar elementos HTML <head> como title y meta usando el soporte SEO integrado.

Los metadatos pueden definirse exportando un objeto metadata o una función generateMetadata en un archivo layout.js o page.js.

import { Metadata } from 'next'

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

export default function Page() {
  return '...'
}
export const metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}

Es bueno saber: No deberías añadir manualmente etiquetas <head> como <title> y <meta> a los layouts raíz. En su lugar, usa la API de Metadatos (Metadata API) que maneja automáticamente requisitos avanzados como streaming y deduplicación de elementos <head>.

Aprende más sobre las opciones de metadatos disponibles en la referencia de API.