Stash da Ana
nextjsseoopen-graphtutorial

Open Graph: como seu link fica bonito ao ser compartilhado

3 min de leitura

Cole o link de um post em qualquer mensageiro ou rede social e o resultado vai depender muito do que está escondido no <head> do HTML. Sem nenhuma instrução, o WhatsApp tenta adivinhar: pega a primeira imagem grande que encontra, ou nenhuma. Com as meta tags certas, o link vira um cartão completo, com imagem, título e descrição. Esse protocolo é o Open Graph.

Preview do blog ao compartilhar no WhatsApp O cartão no WhatsApp lê og:title, og:description e og:image direto do HTML.

O mesmo link compartilhado no LinkedIn A renderização muda entre plataformas, mas a fonte é a mesma — daí a importância de fornecer tags consistentes.

O protocolo em quatro tags

Open Graph foi criado pelo Facebook em 2010 e adotado por praticamente todas as plataformas. São meta tags simples no <head>:

<meta property="og:title" content="Context API no React" />
<meta property="og:description" content="Como compartilhar estado sem prop drilling." />
<meta property="og:image" content="https://seusite.com/blog/context-api/og.png" />
<meta property="og:url" content="https://seusite.com/blog/context-api" />

Quando alguém compartilha um link, o crawler da plataforma faz uma requisição para a URL, lê essas tags e monta o preview. Título e descrição podem ser inferidos de <title> e <meta name="description"> se as variantes og: não existirem — mas a imagem não. Sem og:image, o cartão fica sem imagem ou pega qualquer imagem aleatória da página.

O tamanho recomendado é 1200 × 630 pixels. Vale também declarar og:image:width e og:image:height explicitamente: alguns crawlers usam esses valores para decidir o layout antes mesmo de baixar a imagem inteira, o que acelera o preview no primeiro paste.

ImageResponse no Next.js

O Next.js gera essas imagens por convenção: um arquivo chamado opengraph-image.tsx dentro de qualquer pasta de rota produz a imagem OG daquela rota — sem biblioteca externa, sem serviço de terceiros, sem nada no runtime.

import { ImageResponse } from 'next/og'
 
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
 
export default function OgImage() {
  return new ImageResponse(
    <div style={{ background: '#1e1e2e', width: '100%', height: '100%' }}>
      <h1 style={{ color: 'white', fontSize: 80 }}>Meu Site</h1>
    </div>,
    { ...size }
  )
}

O JSX passa por um renderizador baseado em Satori, não pelo browser. Isso significa um subconjunto bem específico do CSS: flexbox funciona, posicionamento absoluto funciona, mas grid completo não, pseudo-elementos não, e custom fonts precisam ser carregadas explicitamente. É o tipo de surpresa que melhor descobrir cedo do que tarde.

Imagens dinâmicas por post

O ganho real aparece quando a imagem reflete o conteúdo da página. Cada post do blog gera o próprio PNG com o título dele:

export default async function PostOgImage({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = getPostBySlug(slug)
 
  return new ImageResponse(
    <div style={{ /* estilos do cartão */ }}>
      <div style={{ fontSize: 64, fontWeight: 800 }}>
        {post.title}
      </div>
      <div style={{ opacity: 0.6 }}>
        Ana Laura · {post.tags.slice(0, 3).join(' · ')}
      </div>
    </div>,
    { width: 1200, height: 630 }
  )
}

getPostBySlug lê o .md correspondente e devolve os metadados; o componente compõe título e tags. Como o blog usa export estático, esses PNGs são gerados uma vez durante o next build e servidos depois como arquivos comuns.

Imagem OG gerada para um post específico do blog O PNG resultante, 1200×630, com o título do post e a lista de tags.

As meta tags no HTML

Quando o opengraph-image.tsx existe, o Next.js injeta as tags relevantes em cada página automaticamente:

<meta property="og:image" content="/blog/context-api-react-sem-prop-drilling/opengraph-image" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="/blog/context-api-react-sem-prop-drilling/opengraph-image" />

O Twitter (agora X) usa um namespace próprio (twitter:), mas o Next.js preenche os dois conjuntos a partir do mesmo arquivo. Não é preciso duplicar nada.