Next.js: SEO & API de Metadados

Última atualização: 2026-08-26

SEO é um motor de tráfego gratuito para sites — metadados corretos podem fazer o Google indexar seu site três vezes mais rápido.

1. O Que Você Vai Aprender



2. Uma História Real de um Gerente de Operações de Conteúdo

(1) Ponto de Dor: Três meses após o lançamento, o Google indexou apenas cinco páginas

Bob é responsável pelas operações de conteúdo do TaskFlow. Três meses após o lançamento do blog, ele havia escrito 50 artigos:

"O Google Search Console mostra que apenas 5 páginas foram indexadas. Ao inspecionar, descobri que todas as páginas têm <title> 'TaskFlow' como título, sem meta description e sem imagem Open Graph — então, quando compartilhadas nas redes sociais, aparecem como links simples."

Problema Impacto Quantificação
Título duplicado Mecanismos de busca não conseguem distinguir entre páginas Apenas 5 de 50 indexadas
Sem imagem OG Sem imagem de pré-visualização para compartilhamento social Taxa de cliques em compartilhamentos: 0,3%
Sem JSON-LD Sem resumo de mídia rica Sem informações adicionais nos resultados de busca
Sem Sitemap Google não consegue rastrear páginas profundas Atraso de indexação de 30 dias

(2) A Solução da API de Metadados do Next.js

Use generateMetadata() para gerar tags SEO únicas e dados estruturados JSON-LD para cada página.

TS
// app/blog/[slug]/page.tsx — SEO de Notícias
import type { Metadata } from 'next'

export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
  const { slug } = await params
  const post = await getPost(slug)

  return {
    title: `${post.title} - Blog do TaskFlow`,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [{ url: post.ogImage, width: 1200, height: 630 }]
    }
  }
}

(3) Resultado

Métrica Antes da Otimização Depois da Otimização Melhoria
Taxa de Indexação do Google 10% 98% 9,8x
Taxa de Cliques em Compartilhamentos 0,3% 2,8% 9,3x
Resultados de Busca com Mídia Rica ❌ Nenhum ✅ Breadcrumbs + Cartões de Artigo
Velocidade de Descoberta de Páginas 30 dias < 24h 30x


3. Metadados Estáticos e Dinâmicos

(1) Exportação Estática

TS
// app/about/page.tsx — Metadados de Página Estática
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Sobre o TaskFlow - Plataforma de Colaboração em Equipe',
  description: 'TaskFlow ajuda mais de 10.000 equipes a colaborar com alta eficiência, oferecendo Gestão de Projetos, Colaboração em Tempo Real e recursos de análise inteligente.',
  keywords: ['Trabalho em Equipe', 'Gestão de Projetos', 'TaskFlow', 'SaaS'],
  authors: [{ name: 'Equipe TaskFlow', url: 'https://taskflow.io' }]
}

(2) Geração Dinâmica

TS
// app/products/[id]/page.tsx — Metadados de Notícias
import type { Metadata, ResolvingMetadata } from 'next'

type Props = { params: Promise<{ id: string }>; searchParams: Promise<{ [key: string]: string | string[] | undefined }> }

export async function generateMetadata({ params, searchParams }: Props, parent: ResolvingMetadata): Promise<Metadata> {
  const { id } = await params
  const product = await fetch(`https://api.taskflow.io/products/${id}`).then(r => r.json())

  return {
    title: `${product.name} - Produtos TaskFlow`,
    description: product.description,
    openGraph: {
      title: product.name,
      description: product.description,
      url: `https://taskflow.io/products/${id}`,
      siteName: 'TaskFlow',
      images: [
        {
          url: product.ogImage,
          width: 1200,
          height: 630,
          alt: product.name
        }
      ],
      locale: 'pt_BR',
      type: 'website'
    },
    twitter: {
      card: 'summary_large_image',
      title: product.name,
      description: product.description,
      images: [product.ogImage]
    },
    alternates: {
      canonical: `https://taskflow.io/products/${id}`,
      languages: {
        'en': `https://taskflow.io/en/products/${id}`,
        'ja': `https://taskflow.io/ja/products/${id}`,
        'ar': `https://taskflow.io/ar/products/${id}`
      }
    }
  }
}

(3) Tabela de Referência Rápida de Campos de Metadados

Campo Finalidade Exemplo
title Título da Página / <title> 'Detalhes do Produto - TaskFlow'
description Resumo para Mecanismos de Busca 'Ferramenta de Gestão de Projetos TaskFlow...'
openGraph Compartilhamento no Facebook / LinkedIn { title, description, images }
twitter Cartão X (Twitter) { card: 'summary_large_image' }
alternates.canonical URL Autoritativa para Prevenir Conteúdo Duplicado https://taskflow.io/page
alternates.languages hreflang multilíngue { 'en': '...' }
robots Comando para rastreadores { index: true, follow: true }

▶ Exemplo: SEO Completo para um Post de Blog

Output:

TEXT 📖 Somente leitura
Função assíncrona executa e retorna dados buscados.
TS
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'

interface Post {
  title: string
  excerpt: string
  ogImage: string
  publishedAt: string
  author: string
  tags: string[]
}

async function getPost(slug: string): Promise<Post> {
  const res = await fetch(`https://api.taskflow.io/blog/${slug}`, { next: { revalidate: 3600 } })
  return res.json()
}

export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
  const { slug } = await params
  const post = await getPost(slug)

  return {
    title: `${post.title} | Blog do TaskFlow`,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt,
      authors: [post.author],
      images: [{ url: post.ogImage, width: 1200, height: 630 }]
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.ogImage]
    },
    keywords: [...post.tags, 'TaskFlow', 'Gestão de Projetos'],
    alternates: { canonical: `https://taskflow.io/blog/${slug}` },
    robots: { index: true, follow: true }
  }
}

Output:

TEXT 📖 Somente leitura
Define tipo(s) TypeScript: Post.

▶ Exemplo: Usando a meta tag robots para controlar rastreadores web

Output:

TEXT 📖 Somente leitura
Define tipo(s) TypeScript: Post.
TS
// app/private/dashboard/page.tsx — Impedir que mecanismos de busca indexem
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Dashboard - TaskFlow',
  robots: {
    index: false,        // Não indexar
    follow: false,       // Não rastrear links
    noarchive: true,     // Não armazenar em cache
    nosnippet: true,     // Não exibir resumo
    nocache: true        // Não armazenar em cache
  }
}

export default function DashboardPage() {
  return <h1>Dashboard Privado</h1>
}
💻 Output (HTML head):

HTML
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />

Output:

TEXT 📖 Somente leitura
Navegador renderiza: cabeçalho "Dashboard Privado". Mecanismos de busca não indexarão esta página — a tag <meta name="robots" content="noindex, nofollow, noarchive, nosnippet"> impede o rastreamento.


4. Dados Estruturados JSON-LD

(1) Três Schemas Comuns

100%
graph TB
    A[Dados Estruturados JSON-LD] --> B[BreadcrumbList<br/>Navegação Breadcrumb]
    A --> C[Article<br/>Detalhes do Artigo]
    A --> D[FAQPage<br/>Perguntas Frequentes]

    B --> E[Exibir o caminho nos resultados de busca]
    C --> F[Painel de Conhecimento + Imagem de Capa]
    D --> G[Resultados de busca exibem Q&A diretamente]

    style A fill:#cce5ff
    style B fill:#d4edda
    style C fill:#d4edda
    style D fill:#d4edda
Schema Caso de Uso Exibição nos Resultados de Busca
BreadcrumbList Todas as Páginas Exibir trilha de breadcrumb (Início > Produtos > Detalhes)
Article Post de Blog Cartão de Artigo (Título + Resumo + Imagem de Capa + Data de Publicação)
FAQ Page Central de Ajuda / Perguntas Frequentes Exibir lista de perguntas e respostas (recolhível)
Product Página de Produto Preço + Estoque + Classificação por Estrelas

(2) Função de Injeção JSON-LD

TS
// lib/jsonld.ts — Ferramentas de Geração JSON-LD
export function breadcrumbJsonld(items: { name: string; url: string }[]) {
  return {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    itemListElement: items.map((item, index) => ({
      '@type': 'ListItem',
      position: index + 1,
      name: item.name,
      item: item.url
    }))
  }
}

export function articleJsonld(post: {
  title: string
  excerpt: string
  url: string
  ogImage: string
  publishedAt: string
  author: string
}) {
  return {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    description: post.excerpt,
    image: post.ogImage,
    datePublished: post.publishedAt,
    author: { '@type': 'Person', name: post.author },
    publisher: { '@type': 'Organization', name: 'TaskFlow', logo: 'https://taskflow.io/logo.png' },
    mainEntityOfPage: { '@type': 'WebPage', '@id': post.url }
  }
}

▶ Exemplo: Página de Artigo JSON-LD + Breadcrumb

Output:

TEXT 📖 Somente leitura
Módulo TypeScript executado com sucesso.
TSX
// app/blog/[slug]/page.tsx — SEO Completo + JSON-LD
import { breadcrumbJsonld, articleJsonld } from '@/lib/jsonld'

export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  const post = await getPost(slug)
  const url = `https://taskflow.io/blog/${slug}`

  const breadcrumb = breadcrumbJsonld([
    { name: 'Início', url: 'https://taskflow.io' },
    { name: 'Blog', url: 'https://taskflow.io/blog' },
    { name: post.title, url }
  ])

  const article = articleJsonld({
    title: post.title,
    excerpt: post.excerpt,
    url,
    ogImage: post.ogImage,
    publishedAt: post.publishedAt,
    author: post.author
  })

  return (
    <>
      {/* Injeção JSON-LD */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumb) }}
      />
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(article) }}
      />

      <article>
        <h1>{post.title}</h1>
        <p>{post.excerpt}</p>
        <div>{post.content}</div>
      </article>
    </>
  )
}

Output:

TEXT 📖 Somente leitura
Renderiza a UI do componente ▶ Exemplo: Página de Artigo JSON-LD + Breadcrumb conforme descrito na seção.
💡 Dica: O Google Search Console oferece uma ferramenta "Teste de Resultados Avançados" que pode verificar se o JSON-LD é analisado corretamente.



5. Sitemap e robots.txt

(1) Geração Dinâmica de Sitemap

TS
// app/sitemap.ts — Sitemap de Notícias (Inclui automaticamente todas as páginas)
import type { MetadataRoute } from 'next'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseUrl = 'https://taskflow.io'

  // Páginas Estáticas
  const staticPages = [
    { url: baseUrl, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: 1.0 },
    { url: `${baseUrl}/about`, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: 0.8 },
    { url: `${baseUrl}/blog`, lastModified: new Date(), changeFrequency: 'weekly' as const, priority: 0.9 },
    { url: `${baseUrl}/pricing`, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: 0.8 },
    { url: `${baseUrl}/contact`, lastModified: new Date(), changeFrequency: 'yearly' as const, priority: 0.5 }
  ]

  // Posts de Blog Dinâmicos (da API)
  const posts = await fetch('https://api.taskflow.io/blog/posts').then(r => r.json())

  const blogPages = posts.map((post: { slug: string; updatedAt: string }) => ({
    url: `${baseUrl}/blog/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: 'weekly' as const,
    priority: 0.7
  }))

  // Páginas Multilíngues
  const locales = ['en', 'ja', 'ar']
  const localizedPages = locales.flatMap((locale) =>
    staticPages.map((page) => ({
      url: `${baseUrl}/${locale}${page.url.replace(baseUrl, '')}`,
      lastModified: page.lastModified,
      changeFrequency: page.changeFrequency,
      priority: page.priority * 0.9,
      alternates: {
        languages: Object.fromEntries(
          locales.map((l) => [l, `${baseUrl}/${l}${page.url.replace(baseUrl, '')}`])
        )
      }
    }))
  )

  return [...staticPages, ...blogPages, ...localizedPages]
}

(2) robots.txt

TS
// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/api/', '/admin/', '/_next/', '/dashboard']
      },
      {
        userAgent: 'GPTBot',
        disallow: '/'
      }
    ],
    sitemap: 'https://taskflow.io/sitemap.xml'
  }
}

▶ Exemplo: Validação de Sitemap Multilíngue

TEXT 📖 Somente leitura
# Trecho do sitemap.xml Gerado
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://taskflow.io/about</loc>
    <lastmod>2026-07-06</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
    <xhtml:link rel="alternate" hreflang="en" href="https://taskflow.io/en/about"/>
    <xhtml:link rel="alternate" hreflang="ja" href="https://taskflow.io/ja/about"/>
    <xhtml:link rel="alternate" hreflang="ar" href="https://taskflow.io/ar/about"/>
  </url>
</urlset>


6. Gerando Imagens OG Dinâmicas

(1) Arquitetura do @vercel/og

100%
graph LR
    A[Compartilhar o link nas redes sociais] --> B[Rastreador web solicita Imagem OG]
    B --> C[@vercel/og Edge Function]
    C --> D[Satori + React]
    D --> E[Renderizar JSX como PNG]
    E --> F[Imagem OG 1200×630]
    F --> G[Facebook / X / LinkedIn Exibem]

    style C fill:#cce5ff
    style D fill:#d4edda
    style E fill:#fff3cd
Biblioteca Função Descrição
@vercel/og Geração de Imagem OG via Edge Function Zero Sobrecarga no Servidor
Satori Conversão JSX → SVG Menos de 1 ms
resvg-wasm Renderização SVG → PNG ~5ms

(2) Instalação e Exemplos Básicos

BASH
npm install @vercel/og
TSX
// app/og/route.tsx — API de Geração de Imagem OG
import { ImageResponse } from '@vercel/og'

export const runtime = 'edge'

export async function GET() {
  return new ImageResponse(
    (
      <div style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        background: 'linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)',
        color: 'white',
        fontSize: 60,
        fontWeight: 700,
        padding: 40
      }}>
        <h1>TaskFlow</h1>
        <p style={{ fontSize: 32, opacity: 0.9 }}>Plataforma de Colaboração em Equipe</p>
      </div>
    ),
    { width: 1200, height: 630 }
  )
}

▶ Exemplo: Imagem OG Dinâmica para Artigo

Output:

TEXT 📖 Somente leitura
Renderiza a UI do componente GET.
TSX
// app/blog/[slug]/og/route.tsx — Imagem OG para Artigos em Destaque
import { ImageResponse } from '@vercel/og'

export const runtime = 'edge'

export async function GET(req: Request, { params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params

  // Obter Informações do Artigo
  const post = await fetch(`https://api.taskflow.io/blog/${slug}`).then(r => r.json())

  return new ImageResponse(
    (
      <div style={{
        width: 1200,
        height: 630,
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'center',
        padding: 60,
        background: 'linear-gradient(135deg, #1e1b4b 0%, #4f46e5 100%)',
        color: 'white'
      }}>
        {/* Tags */}
        <div style={{ display: 'flex', gap: 8 }}>
          {post.tags?.slice(0, 3).map((tag: string) => (
            <span key={tag} style={{
              padding: '4px 12px',
              borderRadius: 20,
              background: 'rgba(255,255,255,0.2)',
              fontSize: 18
            }}>{tag}</span>
          ))}
        </div>

        {/* Título */}
        <h1 style={{ fontSize: 52, margin: '20px 0', lineHeight: 1.2 }}>
          {post.title}
        </h1>

        {/* Autor + Data */}
        <div style={{ display: 'flex', gap: 16, fontSize: 22, opacity: 0.8 }}>
          <span>{post.author}</span>
          <span>{new Date(post.publishedAt).toLocaleDateString('pt-BR')}</span>
        </div>

        {/* Logo */}
        <div style={{ position: 'absolute', bottom: 40, right: 60, fontSize: 28, fontWeight: 'bold' }}>
          Blog do TaskFlow
        </div>
      </div>
    ),
    { width: 1200, height: 630 }
  )
}

Output:

TEXT 📖 Somente leitura
Busca dados no servidor e renderiza uma lista de itens de post.
Conteúdo visível: ))} | Blog do TaskFlow
💡 Dica: O handler de rota da imagem OG está em /blog/[slug]/og/route.tsx, e o link final é https://taskflow.io/blog/nextjs-seo-guide/og. Basta referenciar este endereço em generateMetadata.



7. Exemplo Completo: Implementação Abrangente de SEO Multilíngue + Imagens OG

TSX
// app/layout.tsx — Layout Raiz (Padrão) SEO + Multilíngue
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: { template: '%s | TaskFlow', default: 'TaskFlow - Plataforma de Colaboração em Equipe' },
  description: 'TaskFlow ajudando mais de 10.000 equipes no mundo a colaborar com alta eficiência. Oferece Gestão de Projetos, Colaboração em Tempo Real, Análise Inteligente com IA.',
  openGraph: {
    siteName: 'TaskFlow',
    type: 'website',
    locale: 'pt_BR',
    images: [{ url: 'https://taskflow.io/og-default.png', width: 1200, height: 630 }]
  },
  twitter: { card: 'summary_large_image', site: '@taskflow' },
  robots: { index: true, follow: true },
  alternates: {
    canonical: 'https://taskflow.io',
    languages: {
      'en': 'https://taskflow.io/en',
      'ja': 'https://taskflow.io/ja',
      'ar': 'https://taskflow.io/ar'
    }
  }
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="pt">
      <body>{children}</body>
    </html>
  )
}
TS
// app/sitemap.ts — Sitemap Completo
import type { MetadataRoute } from 'next'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseUrl = 'https://taskflow.io'
  const locales = ['pt', 'en', 'ja', 'ar'] as const
  
  const pages = ['', '/about', '/blog', '/pricing', '/contact']
  
  return pages.flatMap((page) =>
    locales.map((locale) => ({
      url: `${baseUrl}/${locale}${page}`,
      lastModified: new Date(),
      changeFrequency: 'monthly' as const,
      priority: page === '' ? 1.0 : 0.8,
      alternates: {
        languages: Object.fromEntries(locales.map((l) => [l, `${baseUrl}/${l}${page}`]))
      }
    }))
  )
}
TS
// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: '*', allow: '/', disallow: ['/api/', '/admin/', '/dashboard'] },
    sitemap: 'https://taskflow.io/sitemap.xml'
  }
}
TSX
// app/blog/[slug]/page.tsx — A página de artigo consolida todo o SEO
import type { Metadata } from 'next'

type Props = { params: Promise<{ slug: string }> }

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { slug } = await params
  const post = await fetch(`https://api.taskflow.io/blog/${slug}`).then(r => r.json())

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt,
      authors: [post.author],
      images: [{ url: `https://taskflow.io/blog/${slug}/og`, width: 1200, height: 630 }]
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [`https://taskflow.io/blog/${slug}/og`]
    },
    alternates: {
      canonical: `https://taskflow.io/blog/${slug}`,
      languages: {
        en: `https://taskflow.io/en/blog/${slug}`,
        ja: `https://taskflow.io/ja/blog/${slug}`,
        ar: `https://taskflow.io/ar/blog/${slug}`
      }
    }
  }
}

export default async function BlogPostPage({ params }: Props) {
  const { slug } = await params

  return (
    <article>
      {/* Dados Estruturados JSON-LD */}
      <script type="application/ld+json" dangerouslySetInnerHTML={{
        __html: JSON.stringify({
          '@context': 'https://schema.org',
          '@type': 'BreadcrumbList',
          itemListElement: [
            { '@type': 'ListItem', position: 1, name: 'Início', item: 'https://taskflow.io' },
            { '@type': 'ListItem', position: 2, name: 'Blog', item: 'https://taskflow.io/blog' },
            { '@type': 'ListItem', position: 3, name: slug }
          ]
        })
      }} />

      <h1>Título do Artigo</h1>
      <div>Conteúdo do Artigo...</div>
    </article>
  )
}

❓ Perguntas Frequentes

P: Qual é a diferença entre as exportações de generateMetadata() e metadata? R: metadata é uma exportação estática usada para rotas estáticas (como /about). generateMetadata() é uma função assíncrona que gera metadados dinamicamente com base em params / searchParams e é usada para rotas dinâmicas (como /blog/[slug]). As duas não podem ser usadas simultaneamente.

P: Qual é a relação entre JSON-LD e meta tags? R: Meta tags (<title> / <meta name="description">) são tags SEO básicas. JSON-LD (<script type="application/ld+json">) são dados estruturados que permitem ao Google exibir rich snippets (breadcrumbs, classificações por estrelas, FAQs recolhíveis). Os dois são complementares e ambos precisam ser implementados.

P: Gerar imagens OG dinâmicas aumenta a carga do servidor? R: @vercel/og é executado no Edge Runtime (computação de borda), e cada imagem leva apenas ~5 ms para ser gerada, resultando em sobrecarga praticamente zero. Em ambiente de produção, recomendamos habilitar cache CDN (Cache-Control: public, max-age=31536000, immutable).

P: Onde a tag hreflang deve ser configurada? R: Configure em generateMetadata() em alternates.languages. O Next.js injeta automaticamente <link rel="alternate" hreflang="en" href="..."> no <head> da página. Você também precisa incluir a tag xhtml:link correspondente no sitemap.

P: Os campos changeFrequency e priority no sitemap afetam os rankings do Google? R: O Google declarou oficialmente que ignora esses dois campos. Eles ainda têm valor de referência para mecanismos de busca como Bing e Yandex. Recomendamos que seu sitemap inclua todas as páginas e que o campo lastModified seja preciso — isso é importante para que os rastreadores determinem se devem rastrear novamente as páginas.


📖 Resumo


📝 Exercícios

  1. Exercício Básico (⭐): Configure a exportação metadata na rota estática /about, incluindo title, description, Open Graph e cartão do Twitter.

  2. Exercício Avançado (⭐⭐): Implemente SEO completo para uma página de detalhes de notícia: generateMetadata() gere dinamicamente title, description, tags OG e cartão do Twitter, injete dados estruturados Article JSON-LD e liste todas as URLs de artigos no sitemap.

  3. Desafio (⭐⭐⭐): Construa um sistema completo de geração de imagens OG: @vercel/og gere uma imagem de compartilhamento de 1200×630 para cada post do blog, incluindo título, autor e tags; referencie a URL da imagem em generateMetadata(); e, por fim, use o Teste de Resultados Avançados do Google para verificar a correção do JSON-LD e das tags OG.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%