¿Qué hace og-image-kit?
Objetivo: que cada página pública tenga una imagen de 1200×630 píxeles y las etiquetas og:* / twitter:* correctas, y que hayas verificado todo con scripts/check_og.py, no solo escrito el código.
Paso 1 — Detectar el stack
Revisa el repositorio antes de escribir nada:
| Señal | Stack | Ir a |
|---|---|---|
next en package.json y un directorio app/ (o src/app/) | Next.js App Router | Paso 2A |
next solo con pages/ | Next.js Pages Router | Paso 2C (estático) o añadir /api/og con ImageResponse |
astro en package.json | Astro | Paso 2B |
| HTML plano, Hugo, Jekyll, Vite SPA, cualquier otra cosa | Estático | Paso 2C |
También encuentra: la URL base canónica del sitio (variable de entorno como NEXT_PUBLIC_SITE_URL, site en astro.config.*), los colores de la marca, el logotipo y la fuente en uso. Pregunta al usuario solo si no se encuentra nada.
Paso 2A — Next.js App Router
- Asegúrate de que el layout raíz exporte
metadataBase(las URLs de OG deben ser absolutas):export const metadata = { metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000") } - Copia
assets/nextjs/opengraph-image.tsxaapp/opengraph-image.tsx(imagen por defecto del sitio). EditaBRAND, colores y eslogan. - Para rutas dinámicas (posts de blog, productos) cópialo en el segmento de ruta, por ejemplo:
app/blog/[slug]/opengraph-image.tsx, y usaassets/nextjs/dynamic-opengraph-image.tsx, que leeparamsy carga el título desde tu fuente de datos. En Next.js 15+paramses una Promesa — usaawait. - Añade también
twitter-image.tsxsolo si la tarjeta de X debe ser diferente; de lo contrario Next.js reutiliza la imagen de OG cuando configurastwitter: { card: "summary_large_image" }en metadata. - Texto por página: establece
titleydescriptionenmetadata/generateMetadatade cada página. - Fuentes personalizadas: carga un archivo TTF/OTF (no WOFF2) mediante
fetch(new URL("./Inter-Bold.ttf", import.meta.url))oreadFile. El texto CJK necesita un archivo de fuente CJK o se mostrará como cuadros — subconjunta la fuente para reducir el tamaño.
Límites a respetar (next/og / satori): solo flexbox (sin grid), cada div con múltiples hijos necesita display: "flex", mantén el paquete pequeño — la documentación de ImageResponse de Next.js menciona un límite de 500 KB para JSX + CSS + fuentes + imágenes. Las fuentes grandes también ralentizan el renderizado, así que subconjúntalas.
Paso 2B — Astro
Usa assets/astro/og-endpoint.ts como src/pages/og/[...slug].png.ts. Genera un PNG por página en tiempo de compilación con satori + @resvg/resvg-js (npm i satori @resvg/resvg-js), y lo referencias desde tu layout:
<meta property="og:image" content={new URL(`/og/${slug}.png`, Astro.site)} />
Astro.site debe estar configurado en astro.config.mjs. Archivo de fuente: coloca un TTF en src/assets/fonts/.
Paso 2C — Sitios estáticos / cualquier otra cosa
Genera los PNG una sola vez y haz commit:
python3 scripts/generate_og.py --pages pages.json --out public/og --brand "Acme" --bg "#0f172a" --fg "#ffffff" --accent "#38bdf8"
pages.json es una lista de {"slug": "pricing", "title": "...", "subtitle": "..."} (consulta assets/pages.example.json). Requiere Pillow (pip install pillow). Pasa --font ruta/a/fuente.ttf para fuentes de marca o texto CJK (por ejemplo, Noto Sans SC). Luego añade las etiquetas a cada página (plantilla en references/meta-tags.md).
Paso 3 — Etiquetas requeridas (cada página)
<meta property="og:title" content="Título de la página" />
<meta property="og:description" content="Una oración, ≤ ~160 caracteres" />
<meta property="og:image" content="https://ejemplo.com/og/pagina.png" /> <!-- URL absoluta -->
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://ejemplo.com/pagina" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
Next.js genera estas a partir de metadata + opengraph-image.tsx; no escribas duplicados manualmente.
Paso 4 — Verificar (no lo saltes)
Ejecuta contra el sitio en funcionamiento (servidor de desarrollo, despliegue de previsualización o producción):
python3 scripts/check_og.py http://localhost:3000/ http://localhost:3000/pricing
python3 scripts/check_og.py --json https://ejemplo.com/ # legible por máquina
Obtiene cada página, verifica las etiquetas requeridas, que og:image sea absoluta, accesible, con tipo de contenido de imagen, relación ~1.91:1, al menos 600 px de ancho y menos de 5 MB. Código de salida 1 en caso de error. Corrige todo lo marcado como ERROR; los elementos WARN son recomendaciones.
Nota: las plataformas sociales almacenan en caché las vistas previas. Después de corregir, usa el depurador de cada plataforma (LinkedIn Post Inspector, Facebook Sharing Debugger) para que vuelvan a escanear. X ya no tiene un validador público; publica en un borrador/DM para verificar. Las URLs de localhost no pueden ser escaneadas por esas herramientas — despliega primero.
Reglas de diseño para la imagen
- 1200×630, PNG, texto dentro de un margen seguro de ~60 px; título ≤ ~60 caracteres, máximo 3 líneas.
- Alto contraste; se muestra pequeño (~500 px de ancho) en los feeds. Pruébalo con zoom al 50%.
- Incluye nombre/logotipo de la marca; evita texto pequeño (< 28 px a 1200 de ancho).
- Consulta
references/platform-specs.mdpara notas específicas de cada plataforma.
Terminado cuando
- Cada página indexable devuelve og:title, og:description, og:image (absoluta), twitter:card
-
check_og.pysale con 0 para la página de inicio y al menos una página de cada tipo de ruta - Las imágenes se ven bien (abre un PNG y míralo)