Sugerencias
← TIL
~3 min de lectura
#astro#seo#og-images#satori

Cómo generar OG Images rápidas con Satori + Resvg en Astro

Generar imágenes de Open Graph (OG) dinámicas con código ya es una práctica estándar, pero hacerlo mal puede afectar el rendimiento de tu CI/CD o romper tus Edge Functions en producción.

Satori simplifica el diseño porque permite usar JSX y Tailwind como si estuvieras creando un componente normal de React. El problema: Satori no genera una imagen final, produce un SVG. Para que Twitter, LinkedIn o Facebook rendericen correctamente la tarjeta, necesitás convertir ese SVG a PNG con Resvg.

Pipeline OG
TEXT
Satori (JSX → SVG)

Resvg WASM (SVG → PNG)

Edge Function / CDN

Satori dibuja. Resvg renderiza.

Satori + Resvg vs. Puppeteer

Satori + Resvg vs. Puppeteer
RecomendadoSatori + ResvgPuppeteer
RuntimeLigero, sin dependencias grandesRequiere levantar Chromium
RenderizadoSVG → PNG vía WASMBrowser completo
Compatibilidad Edge✓✓
PerformanceMilisegundosCold starts más altos
CSSSoporte parcialSoporte completo
Complejidad de setupBajaAlta

Puppeteer sigue siendo la mejor opción si necesitás soporte CSS completo o layouts extremadamente complejos. Para la mayoría de las OG Images modernas, Satori + Resvg resuelve con un overhead considerablemente menor.

Implementación en Astro

src/pages/og/[slug].png.ts
TS
import satori from "satori";
import { Resvg } from "@resvg/resvg-wasm";
import type { APIRoute } from "astro";

export const GET: APIRoute = async ({ params }) => {
  const { slug } = params;

  const title = slug
    ? slug.replace(/-/g, " ")
    : "Mi Post";

  // 1. Generamos el SVG con Satori
  const svg = await satori(
    <div tw="flex w-full h-full bg-slate-900 items-center justify-center">
      <h1 tw="text-white text-6xl font-bold">
        {title}
      </h1>
    </div>,
    {
      width: 1200,
      height: 630,
      fonts: [
        // Cargar fuente manualmente
      ],
    }
  );

  // 2. Convertimos SVG → PNG
  const resvg = new Resvg(svg, {
    fitTo: {
      mode: "width",
      value: 1200,
    },
  });

  const pngData = resvg.render().asPng();

  // 3. Devolvemos imagen cacheable
  return new Response(pngData, {
    headers: {
      "Content-Type": "image/png",
      "Cache-Control":
        "public, max-age=31536000, immutable",
    },
  });
};

El verdadero cuello de botella

El problema no suele ser Satori.

El problema es el runtime.

Las Edge Functions tienen límites de CPU y memoria agresivos, especialmente en planes gratuitos. Si el layout tiene demasiadas capas, imágenes pesadas o varias fuentes custom, el render puede exceder el tiempo permitido y devolver errores intermitentes.

Cuando el diseño empieza a parecer una landing page comprimida en 1200×630, mover el endpoint a una Serverless Function tradicional en Node.js es la salida más sensata. Agregás algo de cold start; a cambio, tenés CPU sin el límite agresivo del Edge.

La parte interesante para GEO

Las OG Images controlan la capa visual de distribución en redes sociales. Los LLMs no procesan esa capa: procesan estructura semántica. Por eso cumplen funciones distintas y no son intercambiables.

OG Images vs JSON-LD
CapaObjetivo
OG ImagesDistribución visual y CTR
JSON-LDComprensión semántica e indexación AI

El TIL de citabilidad: datos para el index de IA cubre la capa semántica. Y para entender por qué el indexado de Google cambió, el TIL de Google ya no indexa como antes da el contexto estratégico completo.

Enlace copiado al portapapeles