De Pages a App
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
pages
como enapp
- Migrar incrementalmente tu aplicación existente de
pages
aapp
Actualización
Versión de Node.js
La versión mínima de Node.js es ahora v16.14. 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@latest
Versión de ESLint
Si estás usando ESLint, necesitas actualizar tu versión de ESLint:
npm install -D eslint-config-next@latest
Bueno saber: Puede que necesites reiniciar el servidor de ESLint en VS Code para que los cambios de ESLint surtan efecto. Abre la Paleta de Comandos (
cmd+shift+p
en Mac;ctrl+shift+p
en Windows) y buscaESLint: Restart ESLint Server
.
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
pages
aapp
: Una guía paso a paso para ayudarte a migrar incrementalmente del directoriopages
aapp
.
Mejorando nuevas características
Next.js 13 introdujo el nuevo App Router con nuevas características y convenciones. El nuevo Router está disponible en el directorio app
y coexiste con el directorio pages
.
Actualizar a Next.js 13 no requiere usar el nuevo App Router. Puedes continuar 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/image
anext/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 coincida 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 sin problemas:
- Mueve cualquier script
beforeInteractive
que hayas incluido previamente en_document.js
al archivo de diseño raíz (app/layout.tsx
). - La estrategia experimental
worker
aún no funciona enapp
y los scripts con esta estrategia tendrán que eliminarse o modificarse para usar una estrategia diferente (por ejemplo,lazyOnload
). - Los manejadores
onLoad
,onReady
yonError
no 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 integrando CSS de fuentes. 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 integrar CSS todavía funciona 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 App Router → YouTube (16 minutos).
Mudarse al App Router 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 página por página incremental.
- El directorio
app
admite rutas anidadas y diseños. Aprende más. - Usa carpetas anidadas para definir rutas y un archivo especial
page.js
para 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.js
ylayout.js
.- Usa
page.js
para definir la interfaz de usuario única de una ruta. - Usa
layout.js
para definir la interfaz de usuario que se comparte entre múltiples rutas. - Se pueden usar extensiones de archivo
.js
,.jsx
o.tsx
para 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
getServerSideProps
ygetStaticProps
han sido reemplazadas con una nueva API dentro deapp
.getStaticPaths
ha sido reemplazado congenerateStaticParams
. pages/_app.js
ypages/_document.js
han sido reemplazados con un único diseño raízapp/layout.js
. Aprende más.pages/_error.js
ha sido reemplazado con archivos especialeserror.js
más granulares. Aprende más.pages/404.js
ha sido reemplazado con el archivonot-found.js
.pages/api/*
actualmente permanece dentro del directoriopages
.
Paso 1: Creando el directorio app
Actualiza a la última versión de Next.js (requiere 13.4 o superior):
npm install next@latest
Luego, 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
app
debe 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.tsx
ypages/_document.tsx
. - Se pueden usar extensiones
.js
,.jsx
o.tsx
para archivos de diseño.
Para manejar elementos HTML <head>
, puedes usar el soporte SEO incorporado:
import { 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 _app
o _document
existente, puedes copiar los contenidos (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
mientras migras para evitar que tus rutas pages/*
se rompan. Una vez migrado completamente, puedes eliminarlos de forma segura.
Si estás usando cualquier proveedor de contexto de React, deberán moverse a un Componente del Cliente.
Migrando el patrón getLayout()
a Diseños (Opcional)
Next.js recomendaba agregar una propiedad a los componentes de Página 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.getLayout
depages/dashboard/index.js
y sigue los pasos para migrar páginas al directorioapp
.app/dashboard/page.js export default function Page() { return <p>My Page</p> }
-
Mueve los contenidos de
DashboardLayout
a un nuevo Componente del Cliente para retener el comportamiento del directoriopages
.app/dashboard/DashboardLayout.js 'use client' // esta directiva debe estar en la parte superior 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
DashboardLayout
en un nuevo archivolayout.js
dentro 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 las partes no interactivas de
DashboardLayout.js
(Componente del Cliente) alayout.js
(Componente del Servidor) para reducir la cantidad de JavaScript del componente que envías al cliente.
Paso 3: Migrando next/head
En el directorio pages
, el componente React next/head
se usa para manejar elementos HTML <head>
como title
y meta
. En el directorio app
, next/head
se reemplaza con el nuevo soporte SEO incorporado.
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 { 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: Migrar páginas
- Las páginas en el directorio
app
son 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
,getStaticProps
ygetInitialProps
han sido reemplazados por una API más simple. - El directorio
app
utiliza carpetas anidadas para definir rutas y un archivo especialpage.js
para hacer accesible un segmento de ruta públicamente. -
Directorio pages
Directorio app
Ruta index.js
page.js
/
about.js
about/page.js
/about
blog/[slug].js
blog/[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.js
dentro del directorioapp
.
Bueno saber: Este es el camino de migración más fácil 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.tsx
o 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). - Mueve el componente de página exportado por defecto desde
pages/index.js
aapp/home-page.tsx
.
'use client'
// Este es un Componente del Cliente. Recibe datos como props y
// tiene acceso a estado y efectos, al 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>
)
}
'use client'
// Este es un Componente del Cliente. Recibe datos como props y
// tiene acceso a estado y efectos, al 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.tsx
dentro del directorioapp
. Este es un Componente del Servidor por defecto. -
Importa el Componente del Cliente
home-page.tsx
en 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: Migrar 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
useRouter
se importa desdenext/navigation
y tiene un comportamiento diferente al hookuseRouter
enpages
, que se importa desdenext/router
.- El hook
useRouter
importado desdenext/router
no es compatible en el directorioapp
pero puede seguir usándose en el directoriopages
.
- El hook
- El nuevo
useRouter
no devuelve el stringpathname
. Usa el hook separadousePathname
en su lugar. - El nuevo
useRouter
no devuelve el objetoquery
. Usa el hook separadouseSearchParams
en su lugar. - Puedes usar
useSearchParams
yusePathname
juntos 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 en 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:
isFallback
ha sido eliminado porquefallback
ha sido reemplazado.- Los valores
locale
,locales
,defaultLocales
,domainLocales
han sido eliminados porque las características integradas de i18n de Next.js ya no son necesarias en el directorioapp
. Aprende más sobre i18n. basePath
ha sido eliminado. La alternativa no será parte deuseRouter
. Aún no ha sido implementada.asPath
ha sido eliminado porque el concepto deas
ha sido eliminado del nuevo enrutador.isReady
ha 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.
Consulta la referencia de la API de useRouter()
.
Paso 6: Migrar 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 solicitud.
// 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 solicitud.
// 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 prerrenderiza 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 directorio app
, podemos colocar nuestra obtención de datos dentro de nuestros componentes React usando Componentes del Servidor (Server Components). Esto nos permite enviar menos JavaScript al cliente, manteniendo el HTML renderizado desde el servidor.
Al establecer la opción cache
en 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>
)
}
Acceso 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()
: Basada en la API Web Headers, y puede usarse dentro de Componentes del Servidor (Server Components) para recuperar cabeceras de solicitud.cookies()
: Basada en la API Web Cookies, y puede usarse dentro de Componentes del Servidor (Server Components) para recuperar cookies.
// Directorio `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = 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 = cookies().get('theme')
const data = await getData()
return '...'
}
// Directorio `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = 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 = 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 prerrenderizar 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 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/${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 término más descriptivo generate
, 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 engenerateStaticParams
se generan bajo demanda.false
: Los segmentos dinámicos no incluidos engenerateStaticParams
devolverá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 tiempo determinado.
// 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 Route Handlers en el directorio app
.
Los Route Handlers 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 para obtener datos de forma segura. Más información sobre obtención de datos.
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, deberá 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 deberá 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
Codemods
Next.js proporciona transformaciones Codemod para ayudar a actualizar su base de código cuando una función queda obsoleta. Consulte Codemods para más información.