Páginas y Layouts

El Enrutador de Páginas tiene un sistema de rutas basado en archivos que funciona con el concepto de páginas.

Cuando se añade un archivo al directorio pages, automáticamente estará disponible como una ruta.

En Next.js, una página es un Componente de React exportado desde un archivo .js, .jsx, .ts o .tsx en el directorio pages. Cada página está asociada a una ruta basada en su nombre de archivo.

Ejemplo: Si creas pages/about.js que exporta un componente de React como el siguiente, estará accesible en /about.

export default function About() {
  return <div>About</div>
}

Rutas índice

El enrutador dirigirá automáticamente los archivos llamados index a la raíz del directorio.

  • pages/index.js/
  • pages/blog/index.js/blog

Rutas anidadas

El enrutador soporta archivos anidados. Si creas una estructura de carpetas anidadas, los archivos seguirán siendo enrutados de la misma manera.

  • pages/blog/first-post.js/blog/first-post
  • pages/dashboard/settings/username.js/dashboard/settings/username

Páginas con Rutas Dinámicas

Next.js soporta páginas con rutas dinámicas. Por ejemplo, si creas un archivo llamado pages/posts/[id].js, entonces estará accesible en posts/1, posts/2, etc.

Para aprender más sobre enrutamiento dinámico, consulta la documentación de Enrutamiento Dinámico.

Patrón de Layout

El modelo de React nos permite descomponer una página en una serie de componentes. Muchos de estos componentes suelen reutilizarse entre páginas. Por ejemplo, podrías tener la misma barra de navegación y pie de página en cada página.

components/layout.js
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

Ejemplos

Layout Único Compartido con App Personalizada

Si solo tienes un diseño para toda tu aplicación, puedes crear una App Personalizada y envolver tu aplicación con el layout. Como el componente <Layout /> se reutiliza al cambiar de página, su estado se conservará (ej. valores de inputs).

pages/_app.js
import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

Layouts por Página

Si necesitas múltiples layouts, puedes añadir una propiedad getLayout a tu página, permitiendo devolver un componente React para el layout. Esto te permite definir el diseño por página. Como estamos devolviendo una función, podemos tener layouts anidados complejos si se desea.

pages/index.js

import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return (
    /** Tu contenido */
  )
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // Usa el layout definido a nivel de página, si está disponible
  const getLayout = Component.getLayout ?? ((page) => page)

  return getLayout(<Component {...pageProps} />)
}

Al navegar entre páginas, queremos persistir el estado de la página (valores de inputs, posición de scroll, etc.) para una experiencia de Aplicación de Página Única (SPA).

Este patrón de layout permite la persistencia del estado porque el árbol de componentes de React se mantiene entre transiciones de página. Con el árbol de componentes, React puede entender qué elementos han cambiado para preservar el estado.

Nota importante: Este proceso se llama reconciliación, que es cómo React entiende qué elementos han cambiado.

Con TypeScript

Cuando uses TypeScript, primero debes crear un nuevo tipo para tus páginas que incluya una función getLayout. Luego, debes crear un nuevo tipo para tus AppProps que sobrescriba la propiedad Component para usar el tipo creado anteriormente.

import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'

const Page: NextPageWithLayout = () => {
  return <p>hello world</p>
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}

export default Page

Obtención de Datos

Dentro de tu layout, puedes obtener datos en el cliente usando useEffect o una librería como SWR. Como este archivo no es una Página, no puedes usar getStaticProps o getServerSideProps actualmente.

components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  const { data, error } = useSWR('/api/navigation', fetcher)

  if (error) return <div>Error al cargar</div>
  if (!data) return <div>Cargando...</div>

  return (
    <>
      <Navbar links={data.links} />
      <main>{children}</main>
      <Footer />
    </>
  )
}

On this page