Capítulo 141 de 456

opengraph-image and twitter-image

Core Idea

Convenções de arquivo para definir as imagens que aparecem quando um link do site é compartilhado em redes sociais/apps de mensagem, via imagem estática ou geração por código.

Key Concepts

  • opengraph-image: .jpg, .jpeg, .png, .gif, limite de 8MB; gera meta tags og:image*.
  • twitter-image: mesmos formatos, limite de 5MB; gera meta tags twitter:image*.
  • opengraph-image.alt.txt / twitter-image.alt.txt: arquivo .txt companheiro para definir o texto alternativo.
  • Geração por código: opengraph-image.(js|ts|tsx) ou twitter-image.(js|ts|tsx) default-exportando função que retorna Response (tipicamente via ImageResponse de next/og).
  • Config exports: alt (string), size ({width, height}), contentType (MIME type).
  • params prop: promise com os dynamic route params do segmento onde a imagem está colocada; se usar generateImageMetadata, também recebe id como promise.

Code Examples

import { ImageResponse } from 'next/og'

export const alt = 'About Acme'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'

export default async function Image({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await fetch(`https://.../posts/${slug}`).then((res) => res.json())

  return new ImageResponse(
    (
      <div style={{ fontSize: 48, background: 'white', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        {post.title}
      </div>
    ),
    { ...size }
  )
}
  • O que demonstra: imagem OG dinâmica gerada com dado externo baseado no slug da rota.
import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'

const logoData = await readFile(join(process.cwd(), 'logo.png'), 'base64')
const logoSrc = `data:image/png;base64,${logoData}`

export default async function Image() {
  return new ImageResponse(
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <img src={logoSrc} height="100" />
    </div>
  )
}
  • O que demonstra: uso de asset local via Node.js runtime, lido uma vez no module scope (valor previsível, sem depender de request data).

Reference Tables

File conventionTipos suportados (imagem)
opengraph-image.jpg, .jpeg, .png, .gif
twitter-image.jpg, .jpeg, .png, .gif
opengraph-image.alt / twitter-image.alt.txt
RouteURLparams
app/shop/opengraph-image.js/shopundefined
app/shop/[slug]/opengraph-image.js/shop/1Promise<{ slug: '1' }>
app/shop/[tag]/[item]/opengraph-image.js/shop/1/2Promise<{ tag: '1', item: '2' }>

Anti-patterns

  • Exceder os limites de tamanho de arquivo: twitter-image acima de 5MB ou opengraph-image acima de 8MB faz o build falhar.
  • Passar ArrayBuffer para <img src> sem @ts-expect-error: não faz parte da spec HTML; o motor do next/og (Satori) suporta, mas o TypeScript reclama.
  • Ler asset local dentro da função de render a cada chamada: como o asset não depende de request data, leia uma vez no escopo do módulo para manter o valor previsível e evitar I/O repetido.

Key Takeaways

  1. Imagens geradas por código são estaticamente otimizadas por padrão (build time, cacheadas), a menos que usem Request-time APIs ou dados não-cacheados.
  2. generateImageMetadata permite múltiplas variações de imagem no mesmo arquivo.
  3. opengraph-image/twitter-image são Route Handlers especiais que aceitam as mesmas opções de Route Segment Config de páginas/layouts.

Connects To

  • ImageResponse: API central para gerar essas imagens via JSX.
  • generateImageMetadata: gera múltiplas variantes no mesmo arquivo.
  • favicon, icon, and apple-icon: convenção irmã para ícones do app.