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.
Satori (JSX → SVG)
↓
Resvg WASM (SVG → PNG)
↓
Edge Function / CDNSatori dibuja. Resvg renderiza.
Satori + Resvg vs. Puppeteer
| RecomendadoSatori + Resvg | Puppeteer | |
|---|---|---|
| Runtime | Ligero, sin dependencias grandes | Requiere levantar Chromium |
| Renderizado | SVG → PNG vía WASM | Browser completo |
| Compatibilidad Edge | ✓✓ | ✗ |
| Performance | Milisegundos | Cold starts más altos |
| CSS | Soporte parcial | Soporte completo |
| Complejidad de setup | Baja | Alta |
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
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.
| Capa | Objetivo |
|---|---|
| OG Images | Distribución visual y CTR |
| JSON-LD | Comprensió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.