novedades

Live Content Collections: el CMS que actualiza sin rebuild

Astro 6 estabiliza Live Collections para contenido en tiempo real. Análisis honesto de cuándo los necesitas y cuándo el rebuild sigue siendo la respuesta.

Live Content Collections: el CMS que actualiza sin rebuild

Hay una conversación que he tenido con clientes más veces de las que debería ser necesario. Va más o menos así: el cliente tiene un sitio Astro en producción, el equipo editorial está contento con el CMS, pero hay un problema operacional que se repite cada semana. Alguien publica un artículo con un error —un precio equivocado, un nombre mal escrito, una fecha incorrecta— y corregirlo requiere editar en el CMS, esperar a que el webhook dispare el build, esperar a que el build termine (cuatro minutos, ocho minutos, según el tamaño del sitio), y finalmente verificar que la corrección está live. En una situación de crisis —un precio de producto mal publicado un Black Friday— esos ocho minutos son eternos.

La solución existente para equipos con ese problema era siempre ad-hoc: builds más rápidos (optimización del pipeline de CI), deploys más frecuentes (build cada hora sin importar si hay cambios), o simplemente aceptar el delay como parte del proceso. Ninguna de esas opciones atacaba la causa raíz: que el modelo build-time asume que el contenido no cambia entre builds, lo cual es un supuesto falso para muchos equipos editoriales activos.

Las Live Content Collections, estabilizadas en Astro 6 (marzo 2026), atacan directamente esa causa raíz. No son una optimización del pipeline de build. Son un modelo alternativo: el contenido se obtiene del CMS en el momento en que se sirve la página, no cuando se construye el sitio.

El cambio de modelo que hay que entender

Para entender qué resuelven las Live Collections, conviene tener claro qué hace el modelo anterior. Las colecciones build-time ejecutan sus loaders una vez, durante astro build. El resultado se guarda en un data store local. Cuando el sitio sirve páginas, sirve los datos del momento del último build. Si el CMS tiene actualizaciones posteriores al build, esas actualizaciones no son visibles hasta el próximo deploy.

Las Live Collections hacen exactamente lo opuesto: no ejecutan el loader en build-time. En cada request, el loader se ejecuta, obtiene los datos del CMS, los valida con Zod, y los devuelve a la página. El contenido siempre refleja el estado actual del CMS porque siempre viene directo de la fuente, en tiempo real.

La misma API, resultados fundamentalmente distintos.

Definición y uso

Las Live Collections se definen en src/live.config.ts (separado de src/content.config.ts, aunque pueden coexistir en el mismo proyecto):

import { defineLiveCollection } from 'astro:content';
import { z } from 'astro/zod';

const articulos = defineLiveCollection({
  loader: async ({ id }) => {
    // Si se pide un artículo específico
    if (id) {
      const res = await fetch(
        `${import.meta.env.CMS_URL}/articulos/${id}`,
        { headers: { Authorization: `Bearer ${import.meta.env.CMS_TOKEN}` } }
      );
      if (res.status === 404) return null;
      return res.json();
    }

    // Si se pide la colección completa
    const res = await fetch(
      `${import.meta.env.CMS_URL}/articulos?status=published&limit=50`,
      { headers: { Authorization: `Bearer ${import.meta.env.CMS_TOKEN}` } }
    );
    const data = await res.json();
    return data.items;
  },
  schema: z.object({
    id: z.string(),
    titulo: z.string().min(10).max(70),
    resumen: z.string().min(70).max(160),
    contenidoHtml: z.string(),
    autor: z.string(),
    publicadoEn: z.coerce.date(),
    categoria: z.enum(['tecnologia', 'empresa', 'producto']),
    imagenPrincipal: z.string().url(),
  }),
});

export const collections = { articulos };

En las páginas, la consulta usa getLiveCollection y getLiveEntry en lugar de getCollection y getEntry, con manejo de errores integrado:

---
// src/pages/blog/[slug].astro
import { getLiveEntry } from 'astro:content';

const { entry: articulo, error } = await getLiveEntry(
  'articulos',
  Astro.params.slug
);

if (error) {
  console.error('Error obteniendo artículo:', error);
  return Astro.redirect('/blog?error=cms');
}

if (!articulo) {
  return Astro.redirect('/404');
}

const { titulo, resumen, contenidoHtml, autor, publicadoEn } = articulo.data;
---

<article>
  <h1>{titulo}</h1>
  <p class="meta">
    Por {autor} · <time datetime={publicadoEn.toISOString()}>
      {publicadoEn.toLocaleDateString('es-MX', { dateStyle: 'long' })}
    </time>
  </p>
  <div class="contenido" set:html={contenidoHtml} />
</article>

La validación Zod ocurre en cada request, antes de que los datos lleguen al componente. Si el CMS devuelve un campo publicadoEn como string no parseable como fecha, el error se captura en el bloque { entry, error } y la página puede manejar la situación graciosamente —redirigir, mostrar contenido alternativo— en lugar de explotar con un error de runtime.

La pregunta que más importa: ¿necesitas esto?

Antes de migrar todo tu proyecto a Live Collections, vale la pena ser honesto sobre si las necesitas. Mi criterio:

Necesitas Live Collections si: tu equipo editorial publica o corrige contenido múltiples veces al día y el delay de rebuild crea fricción operacional real; si tienes contenido con urgencia editorial (precios, noticias breaking, disponibilidad de stock); o si el tiempo de build ya supera los dos minutos y sigue creciendo con el volumen de contenido.

No las necesitas si: tu sitio se actualiza una o dos veces al día y el rebuild es automático vía webhook; si la mayor parte del contenido es genuinamente estático (documentación, guías, páginas de producto que no cambian frecuentemente); o si el rendimiento máximo —páginas servidas como HTML estático desde un CDN global— es más importante que la frescura inmediata del contenido.

La verdad incómoda que noto en discusiones sobre Live Collections es que muchos equipos creen que las necesitan cuando en realidad lo que necesitan son builds más rápidos. Un pipeline de CI optimizado que reconstruye el sitio en 90 segundos desde un webhook de CMS satisface la mayoría de los casos de uso “tiempo real” que mencionan equipos editoriales. Antes de adoptar Live Collections —con su complejidad de servidor y sus implicaciones de costo— merece la pena explorar si Sätteri (el nuevo procesador Markdown en Rust de Astro 6) reduciría suficientemente el tiempo de build.

El tradeoff de latencia que no puedes ignorar

Cada página que usa Live Collections hace al menos una solicitud de red al CMS en cada visita. Si tu CMS responde en 150ms de promedio, todas las páginas live agregan 150ms al TTFB. Para un portal con mucho tráfico, ese overhead puede tener consecuencias en Core Web Vitals y en costos de API (si el CMS cobra por solicitud o por volumen).

Astro 6 integra Route Caching experimental precisamente para mitigar esto. Con Route Caching habilitado, puedes declarar cuánto tiempo cachear una respuesta y qué invalida ese caché:

---
import { getLiveEntry } from 'astro:content';

const { entry: producto } = await getLiveEntry('productos', Astro.params.slug);

// Cachear esta página por 2 minutos
// Cuando el producto cambie en el CMS, el caché se invalida automáticamente
Astro.cache.set(producto, { maxAge: 120, swr: 60 });
---

El sistema registra que esta ruta depende de esa entrada específica de la colección productos. Cuando el CMS actualiza ese producto, el caché de esta página se invalida. No tienes que implementar la lógica de invalidación tú mismo. Eso es genuinamente elegante.

Pero Route Caching sigue siendo experimental en la versión actual de Astro 6 y su comportamiento en todos los adapters de deployment (Cloudflare, Vercel, Netlify, Node) no es completamente uniforme aún. Para proyectos en producción que dependen de Live Collections con alto tráfico, recomiendo esperar a que Route Caching sea estable antes de apostar por esa combinación.

La coexistencia con colecciones build-time

Lo que más me parece valioso del diseño de Live Collections es que no reemplaza las colecciones build-time: coexiste con ellas en el mismo proyecto. Puedes tener un content.config.ts con tu blog (build-time, estático, máximo rendimiento) y un live.config.ts con el catálogo de productos (live, siempre fresco, validado en cada request). Las dos coexisten, las dos usan Zod, las dos exponen APIs similares (getCollection vs getLiveCollection).

Esa flexibilidad es la que permite migraciones incrementales. No tienes que decidir si tu sitio es “estático” o “dinámico” como categoría global. Puedes ser estático en las partes que se benefician de ello y live en las partes que lo necesitan.

Mi conclusión: la feature correcta para el problema correcto

Las Live Content Collections representan la madurez de Astro como plataforma para sitios de contenido serio, no solo para proyectos personales o blogs de bajo tráfico. La posibilidad de tener un CMS headless cuyo contenido se refleja en el sitio instantáneamente —sin webhooks, sin builds, sin delays— es un argumento de venta real para agencias que trabajan con clientes editorialmente activos.

Lo que me importa más que la feature en sí es el modelo de datos que representa: unificar colecciones build-time y live bajo la misma API —mismos loaders, mismo schema Zod, misma forma de consultarlas en páginas— significa que la decisión de cuándo usar cada una puede cambiar en el futuro sin reescribir la lógica de las páginas. Hoy puedes empezar con build-time y migrar a live cuando el equipo editorial lo exija. Ese tipo de flexibilidad sin fricción es lo que hace que un framework valga para proyectos a largo plazo.

El único consejo que daría a cualquiera que contemple adoptar Live Collections: mide primero si realmente las necesitas, o si lo que necesitas son builds más rápidos. Las dos opciones son válidas. Pero son soluciones a problemas distintos, y elegir la incorrecta añade complejidad innecesaria.

¿Listo para dar el siguiente paso?

Cuéntanos qué necesitas y te respondemos hoy mismo.

¿Necesitas ayuda?