Cómo implementar Regeneración Estática Incremental (ISR)
La Regeneración Estática Incremental (ISR) permite:
- Actualizar contenido estático sin reconstruir todo el sitio
- Reducir la carga del servidor sirviendo páginas estáticas prerrenderizadas para la mayoría de solicitudes
- Asegurar que los encabezados
cache-control
adecuados se añadan automáticamente a las páginas - Manejar grandes cantidades de páginas de contenido sin tiempos largos de
next build
Aquí un ejemplo mínimo:
interface Post {
id: string
title: string
content: string
}
// Next.js invalidará la caché cuando llegue una
// solicitud, como máximo una vez cada 60 segundos.
export const revalidate = 60
// Prerrenderizaremos solo los parámetros de `generateStaticParams` en tiempo de compilación.
// Si llega una solicitud para una ruta no generada,
// Next.js renderizará la página en el servidor bajo demanda.
export const dynamicParams = true // o false, para mostrar 404 en rutas desconocidas
export async function generateStaticParams() {
const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: String(post.id),
}))
}
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
(res) => res.json()
)
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}
// Next.js invalidará la caché cuando llegue una
// solicitud, como máximo una vez cada 60 segundos.
export const revalidate = 60
// Prerrenderizaremos solo los parámetros de `generateStaticParams` en tiempo de compilación.
// Si llega una solicitud para una ruta no generada,
// Next.js renderizará la página en el servidor bajo demanda.
export const dynamicParams = true // o false, para mostrar 404 en rutas desconocidas
export async function generateStaticParams() {
const posts = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: String(post.id),
}))
}
export default async function Page({ params }) {
const { id } = await params
const post = await fetch(`https://api.vercel.app/blog/${id}`).then((res) =>
res.json()
)
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}
Así funciona este ejemplo:
- Durante
next build
, se generan todas las entradas de blog conocidas (hay 25 en este ejemplo) - Todas las solicitudes a estas páginas (ej.
/blog/1
) se almacenan en caché y son instantáneas - Después de 60 segundos, la siguiente solicitud mostrará la página en caché (obsoleta)
- La caché se invalida y comienza a generarse una nueva versión de la página en segundo plano
- Una vez generada con éxito, Next.js mostrará y almacenará en caché la página actualizada
- Si se solicita
/blog/26
, Next.js generará y almacenará esta página bajo demanda
Referencia
Configuración del segmento de ruta
Funciones
Ejemplos
Revalidación basada en tiempo
Esto obtiene y muestra una lista de entradas de blog en /blog
. Después de una hora, la caché para esta página se invalida en la próxima visita. Luego, en segundo plano, se genera una nueva versión con las entradas más recientes.
interface Post {
id: string
title: string
content: string
}
export const revalidate = 3600 // invalida cada hora
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts: Post[] = await data.json()
return (
<main>
<h1>Entradas del Blog</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
export const revalidate = 3600 // invalida cada hora
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<main>
<h1>Entradas del Blog</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
Recomendamos establecer un tiempo alto de revalidación. Por ejemplo, 1 hora en lugar de 1 segundo. Si necesita más precisión, considere usar revalidación bajo demanda. Para datos en tiempo real, considere cambiar a renderizado dinámico.
Revalidación bajo demanda con revalidatePath
Para un método más preciso de revalidación, invalide páginas bajo demanda con la función revalidatePath
.
Por ejemplo, esta Acción de Servidor se llamaría después de agregar una nueva entrada. Independientemente de cómo obtenga los datos en su Componente de Servidor, ya sea usando fetch
o conectando a una base de datos, esto limpiará la caché para toda la ruta y permitirá al Componente de Servidor obtener datos frescos.
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// Invalida la ruta /posts en la caché
revalidatePath('/posts')
}
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// Invalida la ruta /posts en la caché
revalidatePath('/posts')
}
Ver demo y explorar código fuente.
Revalidación bajo demanda con revalidateTag
Para la mayoría de casos, prefiera revalidar rutas completas. Si necesita control más granular, use revalidateTag
. Por ejemplo, puede etiquetar llamadas fetch
individuales:
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}
Si usa un ORM o conecta a una base de datos, puede usar unstable_cache
:
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
export default async function Page() {
const posts = getCachedPosts()
// ...
}
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
export default async function Page() {
const posts = getCachedPosts()
// ...
}
Luego puede usar revalidateTag
en Acciones de Servidor o Manejadores de Ruta:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// Invalida todos los datos etiquetados con 'posts' en la caché
revalidateTag('posts')
}
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
// Invalida todos los datos etiquetados con 'posts' en la caché
revalidateTag('posts')
}
Manejo de excepciones no capturadas
Si ocurre un error al intentar revalidar datos, se seguirán sirviendo los últimos datos generados exitosamente desde la caché. En la siguiente solicitud, Next.js reintentará revalidar. Más sobre manejo de errores.
Personalizando la ubicación de la caché
Puede configurar la ubicación de la caché de Next.js si desea persistir páginas y datos en almacenamiento duradero, o compartir la caché entre múltiples contenedores o instancias de su aplicación. Más información.
Solución de problemas
Depuración de datos en caché en desarrollo local
Si usa la API fetch
, puede añadir registro adicional para entender qué solicitudes están en caché o no. Más sobre la opción logging
.
module.exports = {
logging: {
fetches: {
fullUrl: true,
},
},
}
Verificar el comportamiento correcto en producción
Para verificar que sus páginas se almacenan en caché y se revalidan correctamente en producción, puede probar localmente ejecutando next build
y luego next start
para ejecutar el servidor de producción de Next.js.
Esto le permitirá probar el comportamiento de ISR (Regeneración Incremental Estática) como funcionaría en un entorno de producción. Para depuración adicional, agregue la siguiente variable de entorno a su archivo .env
:
NEXT_PRIVATE_DEBUG_CACHE=1
Esto hará que el servidor de Next.js registre en la consola los aciertos y fallos de la caché ISR. Puede inspeccionar la salida para ver qué páginas se generan durante next build
, así como cómo se actualizan las páginas cuando se accede a las rutas bajo demanda.
Consideraciones
- ISR solo es compatible cuando se utiliza el entorno de ejecución Node.js (predeterminado).
- ISR no es compatible al crear una Exportación Estática.
- Si tiene múltiples solicitudes
fetch
en una ruta renderizada estáticamente, y cada una tiene una frecuencia derevalidate
diferente, se utilizará el tiempo más bajo para ISR. Sin embargo, esas frecuencias de revalidación seguirán siendo respetadas por la Caché de Datos. - Si alguna de las solicitudes
fetch
utilizadas en una ruta tiene un tiempo derevalidate
de0
, o unno-store
explícito, la ruta se renderizará dinámicamente. - El Middleware no se ejecutará para solicitudes ISR bajo demanda, lo que significa que cualquier reescritura de ruta o lógica en el Middleware no se aplicará. Asegúrese de revalidar la ruta exacta. Por ejemplo,
/post/1
en lugar de una reescritura/post-1
.
Compatibilidad con plataformas
Opción de despliegue | Compatible |
---|---|
Servidor Node.js | Sí |
Contenedor Docker | Sí |
Exportación estática | No |
Adaptadores | Depende de la plataforma |
Aprenda cómo configurar ISR al alojar Next.js usted mismo.
Historial de versiones
Versión | Cambios |
---|---|
v14.1.0 | El cacheHandler personalizado es estable. |
v13.0.0 | Se introduce el App Router. |
v12.2.0 | Pages Router: ISR bajo demanda es estable |
v12.0.0 | Pages Router: Se añade ISR con detección de bots. |
v9.5.0 | Pages Router: Se introduce ISR estable. |