Cómo migrar de Pages al App Router
Esta guía te ayudará a:
- Actualizar tu aplicación Next.js de la versión 12 a la versión 13
- Mejorar características que funcionan tanto en el directorio
pagescomo enapp - Migrar incrementalmente tu aplicación existente de
pagesaapp
Actualización
Versión de Node.js
La versión mínima de Node.js es ahora v18.17. Consulta la documentación de Node.js para más información.
Versión de Next.js
Para actualizar a Next.js versión 13, ejecuta el siguiente comando usando tu gestor de paquetes preferido:
npm install next@latest react@latest react-dom@latestVersión de ESLint
Si estás usando ESLint, necesitas actualizar tu versión de ESLint:
npm install -D eslint-config-next@latestBueno saber: Puede que necesites reiniciar el servidor de ESLint en VS Code para que los cambios surtan efecto. Abre la Paleta de Comandos (
cmd+shift+pen Mac;ctrl+shift+pen Windows) y buscaESLint: Reiniciar servidor ESLint.
Próximos pasos
Después de actualizar, consulta las siguientes secciones para los próximos pasos:
- Mejorar nuevas características: Una guía para ayudarte a actualizar a nuevas características como los componentes mejorados de Image y Link.
- Migrar del directorio
pagesaapp: Una guía paso a paso para ayudarte a migrar incrementalmente del directoriopagesaapp.
Mejorando nuevas características
Next.js 13 introdujo el nuevo Enrutador de Aplicación (App Router) con nuevas características y convenciones. El nuevo Enrutador está disponible en el directorio app y coexiste con el directorio pages.
Actualizar a Next.js 13 no requiere usar el Enrutador de Aplicación. Puedes seguir usando pages con nuevas características que funcionan en ambos directorios, como el componente Image actualizado, el componente Link, el componente Script y la optimización de fuentes.
Componente <Image/>
Next.js 12 introdujo mejoras al Componente Image con una importación temporal: next/future/image. Estas mejoras incluían menos JavaScript del lado del cliente, formas más fáciles de extender y estilizar imágenes, mejor accesibilidad y carga diferida nativa del navegador.
En la versión 13, este nuevo comportamiento es ahora el predeterminado para next/image.
Hay dos codemods para ayudarte a migrar al nuevo Componente Image:
- Codemod
next-image-to-legacy-image: Renombra de forma segura y automática las importaciones denext/imageanext/legacy/image. Los componentes existentes mantendrán el mismo comportamiento. - Codemod
next-image-experimental: Agrega peligrosamente estilos en línea y elimina props no utilizados. Esto cambiará el comportamiento de los componentes existentes para que coincidan con los nuevos valores predeterminados. Para usar este codemod, primero debes ejecutar el codemodnext-image-to-legacy-image.
Componente <Link>
El Componente <Link> ya no requiere agregar manualmente una etiqueta <a> como hijo. Este comportamiento se agregó como una opción experimental en la versión 12.2 y ahora es el predeterminado. En Next.js 13, <Link> siempre renderiza <a> y te permite pasar props a la etiqueta subyacente.
Por ejemplo:
import Link from 'next/link'
// Next.js 12: `<a>` debe estar anidada, de lo contrario se excluye
<Link href="/about">
<a>About</a>
</Link>
// Next.js 13: `<Link>` siempre renderiza `<a>` internamente
<Link href="/about">
About
</Link>Para actualizar tus enlaces a Next.js 13, puedes usar el codemod new-link.
Componente <Script>
El comportamiento de next/script se ha actualizado para soportar tanto pages como app, pero se necesitan algunos cambios para garantizar una migración fluida:
- Mueve cualquier script
beforeInteractiveque hayas incluido previamente en_document.jsal archivo de diseño raíz (app/layout.tsx). - La estrategia experimental
workeraún no funciona enappy los scripts con esta estrategia deberán eliminarse o modificarse para usar una estrategia diferente (por ejemplo,lazyOnload). - Los manejadores
onLoad,onReadyyonErrorno funcionarán en Componentes del Servidor, así que asegúrate de moverlos a un Componente del Cliente o eliminarlos por completo.
Optimización de fuentes
Anteriormente, Next.js te ayudaba a optimizar fuentes mediante CSS en línea. La versión 13 introduce el nuevo módulo next/font que te permite personalizar tu experiencia de carga de fuentes mientras garantiza un gran rendimiento y privacidad. next/font es compatible tanto con los directorios pages como app.
Si bien CSS en línea sigue funcionando en pages, no funciona en app. Debes usar next/font en su lugar.
Consulta la página Optimización de fuentes para aprender a usar next/font.
Migrando de pages a app
🎥 Mira: Aprende cómo adoptar incrementalmente el Enrutador de Aplicación → YouTube (16 minutos).
Mudarte al Enrutador de Aplicación puede ser la primera vez que uses características de React en las que Next.js se basa, como Componentes del Servidor, Suspense y más. Cuando se combinan con nuevas características de Next.js como archivos especiales y diseños, la migración implica nuevos conceptos, modelos mentales y cambios de comportamiento que aprender.
Recomendamos reducir la complejidad combinada de estas actualizaciones dividiendo tu migración en pasos más pequeños. El directorio app está diseñado intencionalmente para funcionar simultáneamente con el directorio pages para permitir una migración incremental página por página.
- El directorio
appadmite rutas anidadas y diseños. Aprende más. - Usa carpetas anidadas para definir rutas y un archivo especial
page.jspara hacer que un segmento de ruta sea accesible públicamente. Aprende más. - Se usan convenciones de archivos especiales para crear la interfaz de usuario para cada segmento de ruta. Los archivos especiales más comunes son
page.jsylayout.js.- Usa
page.jspara definir la interfaz de usuario única de una ruta. - Usa
layout.jspara definir la interfaz de usuario que se comparte entre múltiples rutas. - Se pueden usar extensiones de archivo
.js,.jsxo.tsxpara archivos especiales.
- Usa
- Puedes colocar otros archivos dentro del directorio
app, como componentes, estilos, pruebas y más. Aprende más. - Las funciones de obtención de datos como
getServerSidePropsygetStaticPropshan sido reemplazadas por una nueva API dentro deapp.getStaticPathsha sido reemplazado porgenerateStaticParams. pages/_app.jsypages/_document.jshan sido reemplazados por un único diseño raízapp/layout.js. Aprende más.pages/_error.jsha sido reemplazado por archivos especialeserror.jsmás granulares. Aprende más.pages/404.jsha sido reemplazado por el archivonot-found.js.- Las Rutas API
pages/api/*han sido reemplazadas por el archivo especialroute.js(Manejador de Ruta).
Paso 1: Creando el directorio app
Actualiza a la última versión de Next.js (requiere 13.4 o superior):
npm install next@latestLuego, crea un nuevo directorio app en la raíz de tu proyecto (o directorio src/).
Paso 2: Creando un Diseño Raíz
Crea un nuevo archivo app/layout.tsx dentro del directorio app. Este es un diseño raíz que se aplicará a todas las rutas dentro de app.
export default function RootLayout({
// Los diseños deben aceptar una prop children.
// Esto se llenará con diseños anidados o páginas
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}export default function RootLayout({
// Los diseños deben aceptar una prop children.
// Esto se llenará con diseños anidados o páginas
children,
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}- El directorio
appdebe incluir un diseño raíz. - El diseño raíz debe definir las etiquetas
<html>y<body>ya que Next.js no las crea automáticamente. - El diseño raíz reemplaza los archivos
pages/_app.tsxypages/_document.tsx. - Se pueden usar extensiones
.js,.jsxo.tsxpara archivos de diseño.
Para gestionar elementos HTML <head>, puedes usar el soporte SEO integrado:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}export const metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}Migrando _document.js y _app.js
Si tienes un archivo existente _app o _document, puedes copiar el contenido (por ejemplo, estilos globales) al diseño raíz (app/layout.tsx). Los estilos en app/layout.tsx no se aplicarán a pages/*. Debes mantener _app/_document durante la migración para evitar que tus rutas pages/* se rompan. Una vez migrado completamente, puedes eliminarlos de forma segura.
Si estás usando proveedores de contexto de React, deberás moverlos a un Componente del Cliente.
Migrando el patrón getLayout() a Diseños (Opcional)
Next.js recomendaba agregar una propiedad a los componentes Page para lograr diseños por página en el directorio pages. Este patrón puede reemplazarse con el soporte nativo para diseños anidados en el directorio app.
Ver ejemplo antes y después
Antes
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
}import DashboardLayout from '../components/DashboardLayout'
export default function Page() {
return <p>My Page</p>
}
Page.getLayout = function getLayout(page) {
return <DashboardLayout>{page}</DashboardLayout>
}Después
-
Elimina la propiedad
Page.getLayoutdepages/dashboard/index.jsy sigue los pasos para migrar páginas al directorioapp.app/dashboard/page.js export default function Page() { return <p>My Page</p> } -
Mueve el contenido de
DashboardLayouta un nuevo Componente del Cliente para mantener el comportamiento del directoriopages.app/dashboard/DashboardLayout.js 'use client' // esta directiva debe estar al inicio del archivo, antes de cualquier importación. // Este es un Componente del Cliente export default function DashboardLayout({ children }) { return ( <div> <h2>My Dashboard</h2> {children} </div> ) } -
Importa
DashboardLayouten un nuevo archivolayout.jsdentro del directorioapp.app/dashboard/layout.js import DashboardLayout from './DashboardLayout' // Este es un Componente del Servidor export default function Layout({ children }) { return <DashboardLayout>{children}</DashboardLayout> } -
Puedes mover incrementalmente partes no interactivas de
DashboardLayout.js(Componente del Cliente) alayout.js(Componente del Servidor) para reducir la cantidad de JavaScript de componente que envías al cliente.
Paso 3: Migrando next/head
En el directorio pages, el componente React next/head se usa para gestionar elementos HTML <head> como title y meta. En el directorio app, next/head se reemplaza con el nuevo soporte SEO integrado.
Antes:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}Después:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}export const metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}Consulta todas las opciones de metadata.
Paso 4: Migración de páginas
- Las páginas en el directorio
appson Componentes del Servidor (Server Components) por defecto. Esto es diferente del directoriopages, donde las páginas son Componentes del Cliente (Client Components). - La obtención de datos (data fetching) ha cambiado en
app.getServerSideProps,getStaticPropsygetInitialPropshan sido reemplazados por una API más simple. - El directorio
apputiliza carpetas anidadas para definir rutas y un archivo especialpage.jspara hacer accesible un segmento de ruta públicamente. -
Directorio pagesDirectorio appRuta index.jspage.js/about.jsabout/page.js/aboutblog/[slug].jsblog/[slug]/page.js/blog/post-1
Recomendamos dividir la migración de una página en dos pasos principales:
- Paso 1: Mover el Componente de Página exportado por defecto a un nuevo Componente del Cliente.
- Paso 2: Importar el nuevo Componente del Cliente en un archivo
page.jsdentro del directorioapp.
Nota importante: Esta es la ruta de migración más sencilla porque tiene el comportamiento más comparable al directorio
pages.
Paso 1: Crear un nuevo Componente del Cliente
- Crea un nuevo archivo separado dentro del directorio
app(por ejemplo,app/home-page.tsxo similar) que exporte un Componente del Cliente. Para definir Componentes del Cliente, añade la directiva'use client'al inicio del archivo (antes de cualquier importación).- Similar al Enrutador de Páginas, hay un paso de optimización para prerenderizar Componentes del Cliente a HTML estático en la carga inicial de la página.
- Mueve el componente de página exportado por defecto desde
pages/index.jsaapp/home-page.tsx.
'use client'
// Este es un Componente del Cliente (igual que los componentes en el directorio `pages`)
// Recibe datos como props, tiene acceso a estado y efectos, y es
// prerenderizado en el servidor durante la carga inicial de la página.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}'use client'
// Este es un Componente del Cliente. Recibe datos como props y
// tiene acceso a estado y efectos, igual que los componentes de Página
// en el directorio `pages`.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}Paso 2: Crear una nueva página
-
Crea un nuevo archivo
app/page.tsxdentro del directorioapp. Este es un Componente del Servidor por defecto. -
Importa el Componente del Cliente
home-page.tsxen la página. -
Si estabas obteniendo datos en
pages/index.js, mueve la lógica de obtención de datos directamente al Componente del Servidor usando las nuevas APIs de obtención de datos. Consulta la guía de actualización de obtención de datos para más detalles.// Importa tu Componente del Cliente import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // Obtén datos directamente en un Componente del Servidor const recentPosts = await getPosts() // Pasa los datos obtenidos a tu Componente del Cliente return <HomePage recentPosts={recentPosts} /> }// Importa tu Componente del Cliente import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // Obtén datos directamente en un Componente del Servidor const recentPosts = await getPosts() // Pasa los datos obtenidos a tu Componente del Cliente return <HomePage recentPosts={recentPosts} /> } -
Si tu página anterior usaba
useRouter, necesitarás actualizar a los nuevos hooks de enrutamiento. Aprende más. -
Inicia tu servidor de desarrollo y visita
http://localhost:3000. Deberías ver tu ruta de índice existente, ahora servida a través del directorio app.
Paso 5: Migración de Hooks de Enrutamiento
Se ha añadido un nuevo enrutador para soportar el nuevo comportamiento en el directorio app.
En app, deberías usar los tres nuevos hooks importados desde next/navigation: useRouter(), usePathname(), y useSearchParams().
- El nuevo hook
useRouterse importa desdenext/navigationy tiene un comportamiento diferente al hookuseRouterenpages, que se importa desdenext/router.- El hook
useRouterimportado desdenext/routerno es compatible con el directorioapppero puede seguir usándose en el directoriopages.
- El hook
- El nuevo
useRouterno devuelve el stringpathname. Usa el hook separadousePathnameen su lugar. - El nuevo
useRouterno devuelve el objetoquery. Los parámetros de búsqueda y los parámetros de ruta dinámica ahora están separados. Usa los hooksuseSearchParamsyuseParamsen su lugar. - Puedes usar
useSearchParamsyusePathnamejuntos para escuchar cambios de página. Consulta la sección Eventos del Enrutador (Router Events) para más detalles. - Estos nuevos hooks solo son compatibles con Componentes del Cliente. No pueden usarse en Componentes del Servidor.
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}Además, el nuevo hook useRouter tiene los siguientes cambios:
isFallbackha sido eliminado porquefallbackha sido reemplazado.- Los valores
locale,locales,defaultLocales,domainLocaleshan sido eliminados porque las características integradas de i18n de Next.js ya no son necesarias en el directorioapp. Aprende más sobre i18n. basePathha sido eliminado. La alternativa no será parte deuseRouter. Aún no ha sido implementada.asPathha sido eliminado porque el concepto deasha sido eliminado del nuevo enrutador.isReadyha sido eliminado porque ya no es necesario. Durante el renderizado estático (static rendering), cualquier componente que use el hookuseSearchParams()omitirá el paso de prerenderizado y en su lugar se renderizará en el cliente en tiempo de ejecución.routeha sido eliminado.usePathnameouseSelectedLayoutSegments()proporcionan una alternativa.
Consulta la referencia de la API de useRouter().
Compartiendo componentes entre pages y app
Para mantener componentes compatibles entre los enrutadores pages y app, consulta el hook useRouter desde next/compat/router.
Este es el hook useRouter del directorio pages, pero destinado a usarse mientras se comparten componentes entre enrutadores. Una vez que estés listo para usarlo solo en el enrutador app, actualiza al nuevo useRouter desde next/navigation.
Paso 6: Migración de Métodos de Obtención de Datos
El directorio pages usa getServerSideProps y getStaticProps para obtener datos para las páginas. Dentro del directorio app, estas funciones anteriores de obtención de datos son reemplazadas por una API más simple construida sobre fetch() y Componentes del Servidor React asíncronos.
export default async function Page() {
// Esta solicitud debe almacenarse en caché hasta que se invalide manualmente.
// Similar a `getStaticProps`.
// `force-cache` es el valor por defecto y puede omitirse.
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// Esta solicitud debe volver a obtenerse en cada petición.
// Similar a `getServerSideProps`.
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// Esta solicitud debe almacenarse en caché con un tiempo de vida de 10 segundos.
// Similar a `getStaticProps` con la opción `revalidate`.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}export default async function Page() {
// Esta solicitud debe almacenarse en caché hasta que se invalide manualmente.
// Similar a `getStaticProps`.
// `force-cache` es el valor por defecto y puede omitirse.
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// Esta solicitud debe volver a obtenerse en cada petición.
// Similar a `getServerSideProps`.
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// Esta solicitud debe almacenarse en caché con un tiempo de vida de 10 segundos.
// Similar a `getStaticProps` con la opción `revalidate`.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}Renderizado del Lado del Servidor (getServerSideProps)
En el directorio pages, getServerSideProps se usa para obtener datos en el servidor y pasar props al componente React exportado por defecto en el archivo. El HTML inicial para la página se prerenderiza desde el servidor, seguido de "hidratar" la página en el navegador (haciéndola interactiva).
// Directorio `pages`
export async function getServerSideProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Dashboard({ projects }) {
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}En el Enrutador de la App, podemos colocar nuestra obtención de datos dentro de nuestros componentes React usando Componentes del Servidor. Esto nos permite enviar menos JavaScript al cliente, manteniendo el HTML renderizado desde el servidor.
Al establecer la opción cache a no-store, podemos indicar que los datos obtenidos nunca deben almacenarse en caché. Esto es similar a getServerSideProps en el directorio pages.
// Directorio `app`
// Esta función puede tener cualquier nombre
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}// Directorio `app`
// Esta función puede tener cualquier nombre
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}Accediendo al Objeto de Solicitud
En el directorio pages, puedes recuperar datos basados en solicitudes según la API HTTP de Node.js.
Por ejemplo, puedes recuperar el objeto req desde getServerSideProps y usarlo para obtener las cookies y cabeceras de la solicitud.
// Directorio `pages`
export async function getServerSideProps({ req, query }) {
const authHeader = req.getHeaders()['authorization'];
const theme = req.cookies['theme'];
return { props: { ... }}
}
export default function Page(props) {
return ...
}El directorio app expone nuevas funciones de solo lectura para recuperar datos de solicitud:
headers: Basado en la API de Cabeceras Web, y puede usarse dentro de Componentes del Servidor para recuperar cabeceras de solicitud.cookies: Basado en la API de Cookies Web, y puede usarse dentro de Componentes del Servidor para recuperar cookies.
// Directorio `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = (await headers()).get('authorization')
return '...'
}
export default async function Page() {
// Puedes usar `cookies` o `headers` dentro de Componentes del Servidor
// directamente o en tu función de obtención de datos
const theme = (await cookies()).get('theme')
const data = await getData()
return '...'
}// Directorio `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = (await headers()).get('authorization')
return '...'
}
export default async function Page() {
// Puedes usar `cookies` o `headers` dentro de Componentes del Servidor
// directamente o en tu función de obtención de datos
const theme = (await cookies()).get('theme')
const data = await getData()
return '...'
}Generación de Sitios Estáticos (getStaticProps)
En el directorio pages, la función getStaticProps se usa para prerenderizar una página en tiempo de compilación. Esta función puede usarse para obtener datos desde una API externa o directamente desde una base de datos, y pasar estos datos a toda la página mientras se genera durante la compilación.
// Directorio `pages`
export async function getStaticProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Index({ projects }) {
return projects.map((project) => <div>{project.name}</div>)
}En el directorio app, la obtención de datos con fetch() tendrá por defecto cache: 'force-cache', lo que almacenará en caché los datos de la solicitud hasta que se invaliden manualmente. Esto es similar a getStaticProps en el directorio pages.
// Directorio `app`
// Esta función puede tener cualquier nombre
async function getProjects() {
const res = await fetch(`https://...`)
const projects = await res.json()
return projects
}
export default async function Index() {
const projects = await getProjects()
return projects.map((project) => <div>{project.name}</div>)
}Rutas dinámicas (getStaticPaths)
En el directorio pages, la función getStaticPaths se utiliza para definir las rutas dinámicas que deben ser pre-renderizadas en el momento de construcción.
// Directorio `pages`
import PostLayout from '@/components/post-layout'
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
}
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
export default function Post({ post }) {
return <PostLayout post={post} />
}En el directorio app, getStaticPaths se reemplaza por generateStaticParams.
generateStaticParams se comporta de manera similar a getStaticPaths, pero tiene una API simplificada para devolver parámetros de ruta y puede usarse dentro de layouts. La forma de retorno de generateStaticParams es un array de segmentos en lugar de un array de objetos param anidados o una cadena de rutas resueltas.
// Directorio `app`
import PostLayout from '@/components/post-layout'
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }]
}
async function getPost(params) {
const res = await fetch(`https://.../posts/${(await params).id}`)
const post = await res.json()
return post
}
export default async function Post({ params }) {
const post = await getPost(params)
return <PostLayout post={post} />
}Usar el nombre generateStaticParams es más apropiado que getStaticPaths para el nuevo modelo en el directorio app. El prefijo get se reemplaza por un generate más descriptivo, que encaja mejor ahora que getStaticProps y getServerSideProps ya no son necesarios. El sufijo Paths se reemplaza por Params, que es más apropiado para el enrutamiento anidado con múltiples segmentos dinámicos.
Reemplazo de fallback
En el directorio pages, la propiedad fallback devuelta por getStaticPaths se utiliza para definir el comportamiento de una página que no se ha pre-renderizado en el momento de construcción. Esta propiedad puede establecerse en true para mostrar una página de respaldo mientras se genera la página, false para mostrar una página 404, o blocking para generar la página en el momento de la solicitud.
// Directorio `pages`
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking'
};
}
export async function getStaticProps({ params }) {
...
}
export default function Post({ post }) {
return ...
}En el directorio app, la propiedad config.dynamicParams controla cómo se manejan los parámetros fuera de generateStaticParams:
true: (predeterminado) Los segmentos dinámicos no incluidos engenerateStaticParamsse generan bajo demanda.false: Los segmentos dinámicos no incluidos engenerateStaticParamsdevolverán un 404.
Esto reemplaza la opción fallback: true | false | 'blocking' de getStaticPaths en el directorio pages. La opción fallback: 'blocking' no está incluida en dynamicParams porque la diferencia entre 'blocking' y true es insignificante con streaming.
// Directorio `app`
export const dynamicParams = true;
export async function generateStaticParams() {
return [...]
}
async function getPost(params) {
...
}
export default async function Post({ params }) {
const post = await getPost(params);
return ...
}Con dynamicParams establecido en true (el valor predeterminado), cuando se solicita un segmento de ruta que no ha sido generado, se renderizará en el servidor y se almacenará en caché.
Regeneración estática incremental (getStaticProps con revalidate)
En el directorio pages, la función getStaticProps permite agregar un campo revalidate para regenerar automáticamente una página después de un cierto tiempo.
// Directorio `pages`
export async function getStaticProps() {
const res = await fetch(`https://.../posts`)
const posts = await res.json()
return {
props: { posts },
revalidate: 60,
}
}
export default function Index({ posts }) {
return (
<Layout>
<PostList posts={posts} />
</Layout>
)
}En el directorio app, la obtención de datos con fetch() puede usar revalidate, que almacenará en caché la solicitud durante la cantidad de segundos especificada.
// Directorio `app`
async function getPosts() {
const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
const data = await res.json()
return data.posts
}
export default async function PostList() {
const posts = await getPosts()
return posts.map((post) => <div>{post.name}</div>)
}Rutas API
Las Rutas API continúan funcionando en el directorio pages/api sin cambios. Sin embargo, han sido reemplazadas por Manejadores de Ruta (Route Handlers) en el directorio app.
Los Manejadores de Ruta permiten crear manejadores de solicitud personalizados para una ruta dada usando las APIs Web Request y Response.
export async function GET(request: Request) {}export async function GET(request) {}Nota importante: Si anteriormente usaba rutas API para llamar a una API externa desde el cliente, ahora puede usar Componentes del Servidor (Server Components) en su lugar para obtener datos de forma segura. Más información sobre obtención de datos.
Aplicaciones de una sola página (SPA)
Si también está migrando a Next.js desde una Aplicación de una sola página (SPA) al mismo tiempo, consulte nuestra documentación para obtener más información.
Paso 7: Estilos
En el directorio pages, las hojas de estilo globales estaban restringidas solo a pages/_app.js. Con el directorio app, esta restricción se ha eliminado. Los estilos globales se pueden agregar a cualquier layout, página o componente.
Tailwind CSS
Si está usando Tailwind CSS, necesitará agregar el directorio app a su archivo tailwind.config.js:
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Agregar esta línea
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}También necesitará importar sus estilos globales en su archivo app/layout.js:
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}Más información sobre estilos con Tailwind CSS
Usar App Router junto con Pages Router
Al navegar entre rutas servidas por los diferentes routers de Next.js, habrá una navegación dura. El pre-fetching automático de enlaces con next/link no pre-fetcheará entre routers.
En su lugar, puede optimizar navegaciones entre App Router y Pages Router para conservar las transiciones de página pre-fetcheadas y rápidas. Más información.
Codemods
Next.js proporciona transformaciones Codemod para ayudar a actualizar su base de código cuando una característica queda obsoleta. Consulte Codemods para más información.