# Prompt para Gerar Imagens OG no Momento da Build

> Prompt para agente de IA gerar imagens Open Graph por página no momento da build em Astro ou Next.js usando Satori e sharp, sem serviço externo.

**Type:** Prompt  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Astro, Next.js, TypeScript  
**Difficulty:** medium  
**Updated:** 2026-06-08

---

Entregue este prompt ao seu agente para gerar uma imagem PNG OG única para cada página no
momento da build usando Satori e sharp — evitando a latência e o custo de serviços de imagem OG
em tempo de execução como Vercel OG.

## Prompt Principal

```txt title="Main Prompt"
You are working in an existing Astro 5 static site with TypeScript.

Task: generate a static Open Graph PNG image for every blog post at build time.

Requirements:
- Install `satori` and `sharp`.
- Create `src/lib/generate-og.ts` that exports `generateOgImage({ title, description }: OgData): Promise<Buffer>`.
  - Use Satori to render a JSX template (800 x 420 px) with: site name top-left, post title
    (large, bold, white), description (smaller, gray), a solid dark background (#0f172a).
  - Convert the Satori SVG output to PNG using `sharp(Buffer.from(svg)).png().toBuffer()`.
  - Use only system-safe fonts or load `src/fonts/Inter-Bold.ttf` (create a note to add the file).
- Create `src/pages/og/[slug].png.ts` as an Astro endpoint:
  - `getStaticPaths`: call `getCollection('blog')` and return a path per post.
  - `GET`: call `generateOgImage` with the post's title and description, set
    `Content-Type: image/png`, return the buffer.
- In the blog post layout, set `<meta property="og:image" content={ogImageUrl} />` where
  `ogImageUrl` is constructed as `/og/${post.slug}.png`.
- Do NOT use `@vercel/og`, `next/og`, or any cloud image generation service.
- Do NOT use Canvas or puppeteer.

Stop and list all planned file changes before writing any code.
```

## Notas de Implementação

- Satori aceita objetos semelhantes a JSX (sintaxe de elemento React), mas NÃO usa o runtime React.
  Passe o elemento como uma árvore de objeto simples usando o primeiro argumento do `satori` diretamente.
- Carregamento de fontes: Satori requer pelo menos uma fonte. Use `fs.readFileSync` para carregar o arquivo `.ttf`
  no momento da build — isso é tranquilo em uma build estática do Astro.
- Os PNGs gerados estarão na pasta `dist/og/` após `astro build`. Verifique o tamanho; em
  800x420 devem ficar abaixo de 100 KB cada se usando um fundo de cor sólida.
- Para Next.js em vez de Astro: use um Route Handler em `app/og/[slug]/route.ts` e chame
  `NextResponse` com o buffer PNG.

## Alterações Esperadas nos Arquivos

```txt
src/lib/generate-og.ts              (new)
src/pages/og/[slug].png.ts          (new — Astro endpoint)
src/layouts/BlogPost.astro          (edited — add og:image meta)
src/fonts/Inter-Bold.ttf            (add manually — not auto-generated)
package.json                        (edited — add satori, sharp)
```

## Critérios de Aceitação

- `astro build` gera um arquivo `.png` dentro de `dist/og/` para cada post do blog.
- Cada PNG tem 800 x 420 pixels.
- O layout do post do blog inclui `og:image` apontando para o caminho correto `/og/<slug>.png`.
- Abrir a URL da imagem OG em um navegador mostra um card estilizado com o título do post.

## Comandos de Teste

```bash
bun add satori sharp
bun run build
ls dist/og/
file dist/og/my-first-post.png   # should output "PNG image data, 800 x 420"
curl -I http://localhost:4321/og/my-first-post.png | grep content-type
```

## Erros Comuns de IA

- Importar React e tentar renderizar com `ReactDOMServer` — Satori não usa o runtime do React.
- Esquecer de carregar uma fonte, o que faz o Satori lançar `"No font found for the first character"`.
- Definir `og:image` para um caminho relativo como `./og/slug.png` — deve ser uma URL absoluta em produção.
- Usar o pacote `canvas` que requer bindings nativos incompatíveis com muitos ambientes de CI.

## Prompt de Correção

```txt title="Fix Prompt"
Satori throws a font error or the PNG is blank. Fix in order:
1. Add font loading: `const fontData = fs.readFileSync('src/fonts/Inter-Bold.ttf'); fonts: [{ name: 'Inter', data: fontData, weight: 700 }]` in the satori call.
2. Make sure the JSX-like element passed to satori is a plain object, not a JSX expression — call `satori(element, options)` directly.
3. For the og:image meta tag, use an absolute URL: `const base = import.meta.env.SITE; ogImageUrl = new URL(\`/og/\${slug}.png\`, base).toString();`
Show only the corrected diff.
```