Regeneración Estática Incremental

Ejemplos

Next.js te permite crear o actualizar páginas estáticas después de haber construido tu sitio. La Regeneración Estática Incremental (ISR) te permite usar generación estática página por página, sin necesidad de reconstruir todo el sitio. Con ISR, puedes mantener los beneficios de lo estático mientras escalas a millones de páginas.

Importante: El runtime edge actualmente no es compatible con ISR, aunque puedes aprovechar stale-while-revalidate configurando manualmente el encabezado cache-control.

Para usar ISR, añade la propiedad revalidate a getStaticProps:

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

// Esta función se ejecuta en el servidor durante el build.
// Puede llamarse nuevamente en una función serverless si
// la revalidación está activada y llega una nueva solicitud
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js intentará regenerar la página:
    // - Cuando llegue una solicitud
    // - Como máximo una vez cada 10 segundos
    revalidate: 10, // En segundos
  }
}

// Esta función se ejecuta en el servidor durante el build.
// Puede llamarse nuevamente en una función serverless si
// la ruta no ha sido generada.
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Obtener las rutas a pre-renderizar basadas en posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // Pre-renderizaremos solo estas rutas durante el build.
  // { fallback: 'blocking' } renderizará páginas en el servidor
  // bajo demanda si la ruta no existe.
  return { paths, fallback: 'blocking' }
}

export default Blog

Cuando se hace una solicitud a una página pre-renderizada durante el build, inicialmente se mostrará la página en caché.

  • Cualquier solicitud posterior a la página antes de que pasen 10 segundos también se sirve desde caché al instante.
  • Pasada la ventana de 10 segundos, la siguiente solicitud seguirá mostrando la página en caché (obsoleta).
  • Next.js activa una regeneración en segundo plano.
  • Una vez que la página se regenera con éxito, Next.js invalidará la caché y mostrará la página actualizada. Si la regeneración falla, la página antigua permanecerá sin cambios.

Cuando se solicita una ruta no generada, Next.js renderizará la página en el servidor en la primera solicitud. Las solicitudes futuras servirán el archivo estático desde la caché. ISR en Vercel persiste la caché globalmente y maneja rollbacks.

Importante: Verifica si tu proveedor de datos tiene caché habilitada por defecto. Puedes necesitar desactivarla (ej. useCdn: false), de lo contrario una revalidación no podrá obtener datos frescos para actualizar la caché ISR. La caché puede ocurrir en un CDN (para un endpoint solicitado) cuando devuelve el encabezado Cache-Control.

Revalidación Bajo Demanda

Si configuras un tiempo revalidate de 60, todos los visitantes verán la misma versión generada de tu sitio durante un minuto. La única forma de invalidar la caché es que alguien visite esa página después de que pase el minuto.

A partir de v12.2.0, Next.js soporta Regeneración Estática Incremental Bajo Demanda para purgar manualmente la caché de Next.js para una página específica. Esto facilita actualizar tu sitio cuando:

  • Se crea o actualiza contenido desde tu CMS headless
  • Cambian metadatos de ecommerce (precio, descripción, categoría, reseñas, etc.)

Dentro de getStaticProps, no necesitas especificar revalidate para usar revalidación bajo demanda. Si revalidate se omite, Next.js usará el valor por defecto false (sin revalidación) y solo revalidará la página bajo demanda cuando se llame a revalidate().

Importante: El Middleware no se ejecutará para solicitudes ISR bajo demanda. En su lugar, llama a revalidate() en la ruta exacta que deseas revalidar. Por ejemplo, si tienes pages/blog/[slug].js y un rewrite de /post-1 -> /blog/post-1, deberás llamar a res.revalidate('/blog/post-1').

Usando Revalidación Bajo Demanda

Primero, crea un token secreto conocido solo por tu app Next.js. Este secreto prevendrá acceso no autorizado a la ruta API de revalidación. Puedes acceder a la ruta (manualmente o con un webhook) con esta estructura de URL:

Terminal
https://<your-site.com>/api/revalidate?secret=<token>

Luego, añade el secreto como una Variable de Entorno a tu aplicación. Finalmente, crea la ruta API de revalidación:

pages/api/revalidate.js
export default async function handler(req, res) {
  // Verificar el secreto para confirmar que es una solicitud válida
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Token inválido' })
  }

  try {
    // esto debe ser la ruta real, no una ruta reescrita
    // ej. para "/blog/[slug]" debe ser "/blog/post-1"
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // Si hay un error, Next.js continuará mostrando
    // la última página generada exitosamente
    return res.status(500).send('Error al revalidar')
  }
}

Ver nuestra demo para ver la revalidación bajo demanda en acción y enviar feedback.

Probando ISR Bajo Demanda durante desarrollo

Al ejecutar localmente con next dev, getStaticProps se invoca en cada solicitud. Para verificar que tu configuración ISR bajo demanda es correcta, necesitarás crear un build de producción e iniciar el servidor de producción:

Terminal
$ next build
$ next start

Luego, puedes confirmar que las páginas estáticas se han revalidado correctamente.

Manejo de errores y revalidación

Si hay un error dentro de getStaticProps durante la regeneración en segundo plano, o lanzas un error manualmente, se seguirá mostrando la última página generada exitosamente. En la siguiente solicitud, Next.js reintentará llamar a getStaticProps.

export async function getStaticProps() {
  // Si esta solicitud lanza un error no capturado, Next.js no
  // invalidará la página actualmente mostrada y
  // reintentará getStaticProps en la siguiente solicitud.
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  if (!res.ok) {
    // Si hay un error del servidor, puedes lanzar un error
    // en lugar de devolver para que la caché no se actualice
    // hasta la próxima solicitud exitosa.
    throw new Error(`Error al obtener posts, estado recibido ${res.status}`)
  }

  // Si la solicitud fue exitosa, devuelve los posts
  // y revalida cada 10 segundos.
  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

Auto-hospedaje de ISR

La Regeneración Estática Incremental (ISR) funciona en sitios Next.js auto-hospedados directamente al usar next start.

Puedes usar este enfoque al desplegar en orquestadores de contenedores como Kubernetes o HashiCorp Nomad. Por defecto, los assets generados se almacenan en memoria en cada pod. Esto significa que cada pod tendrá su propia copia de los archivos estáticos. Puede mostrarse data obsoleta hasta que ese pod específico reciba una solicitud.

Para garantizar consistencia entre todos los pods, puedes desactivar el caché en memoria. Esto hará que el servidor Next.js solo use assets generados por ISR en el sistema de archivos.

Puedes usar un mount de red compartido en tus pods Kubernetes (o configuración similar) para reusar el mismo caché de sistema de archivos entre diferentes contenedores. Al compartir el mismo mount, la carpeta .next que contiene el caché de next/image también se compartirá y reusará.

Para desactivar el caché en memoria, configura isrMemoryCacheSize a 0 en tu archivo next.config.js:

next.config.js
module.exports = {
  experimental: {
    // Por defecto 50MB
    isrMemoryCacheSize: 0, // tamaño del caché en bytes
  },
}

Importante: Puedes necesitar considerar una condición de carrera entre múltiples pods intentando actualizar el caché al mismo tiempo, dependiendo de cómo esté configurado tu mount compartido.

Historial de Versiones

VersiónCambios
v12.2.0ISR Bajo Demanda estable
v12.1.0ISR Bajo Demanda añadido (beta).
v12.0.0Fallback ISR consciente de bots añadido.
v9.5.0Base Path añadido.