Seu build do Next.js compila 91.000 páginas. Cada uma delas carrega schema JSON-LD — marcação Person para perfis de celebridades, dados de Event para 137.000 listagens de locais, gráficos Organization para 25.000 páginas de empresas. Sem atualizações manuais. Sem erros de validação de schema no Search Console. Sem surpresas pós-deploy quando o crawler do Google chega. Fizemos isso em três projetos de produção: Deluxe Astrology (30 idiomas, horóscopo, perfis de celebridades), Not Another Sunday (listagens de locais) e HostList (perfis de empresas). Cada tipo de schema puxa dados de linhas do banco de dados em tempo de build, valida automaticamente e se monitora em produção. O código abaixo é o que realmente roda — não teoria, não exemplos sanitizados. Mas primeiro: por que schema programático quebra para a maioria das equipes e as três escolhas arquitetônicas que impedem isso.

Este não é um artigo "o que é schema markup". Você sabe o que é. Este é o guia de implementação que eu gostaria que tivesse existido quando começamos a conectar dados estruturados a aplicativos Next.js apoiados por Supabase servindo páginas em 30 idiomas.

Sumário

Schema Markup em Next.js: Guia de Dados Estruturados JSON-LD para 2026

Por que Schema Markup Ainda Importa em 2026

Google processa mais de 8,5 bilhões de consultas diariamente. AI Overviews agora aparecem em aproximadamente 30% dos resultados de pesquisa nos EUA. E aqui está o que importa para suas decisões de implementação: dados estruturados é como máquinas entendem suas páginas. Não apenas Google — ChatGPT, Perplexity, Claude e todas as outras ferramentas de pesquisa alimentadas por LLM analisando a web.

O caso de ROI é direto:

Métrica Sem Schema Com Schema Delta Observado
CTR do SERP Baseline +25-35% com rich results +31% em páginas de local Not Another Sunday
Inclusão em AI Overview Baixa Significativamente maior 3.2x mais provável em páginas com anotação FAQ
Taxa de citação por LLM Mínima Mensurável Páginas com schema FAQPage citadas 4x mais por Perplexity
Elegibilidade para rich result Nenhuma Estrelas, FAQs, breadcrumbs, etc. Ativa em 87% das páginas indexadas

Para sites com dezenas de milhares de páginas, schema manual é impossível. Você precisa de um sistema. É isso que este guia constrói.

O Ângulo de Citação por LLM: FAQPage como Ouro Legível por Máquina

Aqui está algo que a maioria dos guias de schema não cobre: schema FAQPage é o formato de máquina mais legível para mecanismos de pesquisa alimentados por LLM. Quando ChatGPT ou Perplexity rastreiam sua página, estão procurando pares Q&A claramente estruturados. Schema FAQPage entrega exatamente isso — pares de pergunta-resposta pré-analisados e inequívocos que não requerem nenhuma extração de PNL.

Notamos esse padrão primeiro em Deluxe Astrology. Páginas com schema FAQPage estavam sendo citadas em respostas do Perplexity em aproximadamente 4x a taxa de páginas equivalentes sem ele. Os pares de Q&A estavam sendo levantados quase literalmente.

Esto não é apenas um jogo de SEO mais. É um jogo de Generative Engine Optimization (GEO). Se você quer que seu conteúdo seja apresentado em respostas geradas por IA — e quer, porque é para lá que a pesquisa está indo — schema FAQPage é seu investimento de maior alavancagem.

Padrão de Implementação Next.js App Router

Vamos entrar no código real. Usamos um padrão consistente em todos os nossos projetos de desenvolvimento Next.js: um componente JsonLd reutilizável renderizado dentro de componentes de servidor.

O Componente Base

// components/json-ld.tsx
export function JsonLd({ data }: { data: Record<string, unknown> }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{
        __html: JSON.stringify({
          '@context': 'https://schema.org',
          ...data,
        }),
      }}
    />
  );
}

Simples. Sem JavaScript do lado do cliente. Sem incompatibilidades de hidratação. Isto renderiza na saída do componente do servidor e é entregue como HTML estático. O crawler do Google o vê imediatamente — nenhuma execução de JavaScript necessária.

Schema em Nível de Layout vs Nível de Página

Dividimos schema em duas categorias:

Nível de layout (renderizado em layout.tsx): Organization, WebSite, BreadcrumbList. Estes são consistentes em páginas ou grupos de páginas.

Nível de página (renderizado em page.tsx): Article, FAQPage, Person, LocalBusiness, Product. Estes são únicos por página e típicamente orientados por conteúdo do banco de dados.

// app/layout.tsx
import { JsonLd } from '@/components/json-ld';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <JsonLd
          data={{
            '@type': 'Organization',
            name: 'Social Animal',
            url: 'https://socialanimal.dev',
            logo: 'https://socialanimal.dev/logo.png',
            sameAs: [
              'https://twitter.com/socialanimaldev',
              'https://github.com/social-animal',
            ],
            contactPoint: {
              '@type': 'ContactPoint',
              contactType: 'sales',
              url: 'https://socialanimal.dev/contact',
            },
          }}
        />
        <JsonLd
          data={{
            '@type': 'WebSite',
            name: 'Social Animal',
            url: 'https://socialanimal.dev',
            potentialAction: {
              '@type': 'SearchAction',
              target: {
                '@type': 'EntryPoint',
                urlTemplate: 'https://socialanimal.dev/search?q={search_term_string}',
              },
              'query-input': 'required name=search_term_string',
            },
          }}
        />
        {children}
      </body>
    </html>
  );
}

Isso significa que cada página no site recebe schema Organization e WebSite sem nenhum trabalho por página. Renderizado no servidor, zero sobrecarga de JS do cliente.

Schema Markup em Next.js: Guia de Dados Estruturados JSON-LD para 2026 - arquitetura

Cada Tipo de Schema Com Código JSON-LD Funcional

Aqui está cada tipo de schema que usamos em produção, com padrões reais de nossos projetos.

Organization

{
  "@type": "Organization",
  "name": "Social Animal",
  "url": "https://socialanimal.dev",
  "logo": "https://socialanimal.dev/logo.png",
  "description": "Agência de desenvolvimento web headless especializada em Next.js e Astro",
  "foundingDate": "2022",
  "sameAs": [
    "https://twitter.com/socialanimaldev",
    "https://linkedin.com/company/socialanimaldev"
  ],
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Remote",
    "addressCountry": "US"
  }
}

WebSite

Mostrado acima no exemplo de layout. O SearchAction é o que alimenta a caixa de pesquisa de sitelinks no Google. Não pule.

Article / BlogPosting

// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug);

  return (
    <article>
      <JsonLd
        data={{
          '@type': 'Article',
          headline: post.title,
          description: post.excerpt,
          image: post.featuredImage,
          datePublished: post.publishedAt,
          dateModified: post.updatedAt,
          author: {
            '@type': 'Organization',
            name: 'Social Animal',
            url: 'https://socialanimal.dev',
          },
          publisher: {
            '@type': 'Organization',
            name: 'Social Animal',
            logo: {
              '@type': 'ImageObject',
              url: 'https://socialanimal.dev/logo.png',
            },
          },
          mainEntityOfPage: {
            '@type': 'WebPage',
            '@id': `https://socialanimal.dev/blog/${post.slug}`,
          },
        }}
      />
      {/* Article content */}
    </article>
  );
}

FAQPage

Este é o grande para citações por LLM:

function buildFaqSchema(faqs: Array<{ question: string; answer: string }>) {
  return {
    '@type': 'FAQPage',
    mainEntity: faqs.map((faq) => ({
      '@type': 'Question',
      name: faq.question,
      acceptedAnswer: {
        '@type': 'Answer',
        text: faq.answer,
      },
    })),
  };
}
function buildBreadcrumbSchema(items: Array<{ name: string; url: string }>) {
  return {
    '@type': 'BreadcrumbList',
    itemListElement: items.map((item, index) => ({
      '@type': 'ListItem',
      position: index + 1,
      name: item.name,
      item: item.url,
    })),
  };
}

// Uso para uma página de local em Not Another Sunday:
<JsonLd
  data={buildBreadcrumbSchema([
    { name: 'Home', url: 'https://notanothersunday.com' },
    { name: 'London', url: 'https://notanothersunday.com/london' },
    { name: 'Restaurants', url: 'https://notanothersunday.com/london/restaurants' },
    { name: venue.name, url: `https://notanothersunday.com/venue/${venue.slug}` },
  ])}
/>

Service

{
  "@type": "Service",
  "name": "Desenvolvimento Next.js",
  "description": "Desenvolvimento customizado Next.js App Router com integração de headless CMS",
  "provider": {
    "@type": "Organization",
    "name": "Social Animal"
  },
  "serviceType": "Web Development",
  "areaServed": "Worldwide",
  "url": "https://socialanimal.dev/capabilities/nextjs-development"
}

LocalBusiness

Isso alimenta as 137.000 listagens de local de Not Another Sunday:

function buildLocalBusinessSchema(venue: Venue) {
  return {
    '@type': venue.type === 'restaurant' ? 'Restaurant' : 'LocalBusiness',
    name: venue.name,
    description: venue.description,
    image: venue.images[0],
    address: {
      '@type': 'PostalAddress',
      streetAddress: venue.address,
      addressLocality: venue.city,
      postalCode: venue.postcode,
      addressCountry: venue.country,
    },
    geo: {
      '@type': 'GeoCoordinates',
      latitude: venue.lat,
      longitude: venue.lng,
    },
    url: venue.website,
    telephone: venue.phone,
    priceRange: venue.priceRange,
    aggregateRating: venue.reviewCount > 0 ? {
      '@type': 'AggregateRating',
      ratingValue: venue.rating,
      reviewCount: venue.reviewCount,
    } : undefined,
  };
}

Product

{
  "@type": "Product",
  "name": "Pacote de Desenvolvimento de Headless CMS",
  "description": "Setup completo de headless CMS com modelagem de conteúdo e integração de API",
  "offers": {
    "@type": "Offer",
    "price": "5000",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "url": "https://socialanimal.dev/pricing"
  }
}

HowTo

{
  "@type": "HowTo",
  "name": "Como Adicionar Schema Markup ao Next.js App Router",
  "description": "Guia passo a passo para implementar dados estruturados JSON-LD em componentes do servidor Next.js",
  "step": [
    {
      "@type": "HowToStep",
      "name": "Crie um componente JsonLd",
      "text": "Construa um componente de servidor reutilizável que renderiza uma tag script com tipo application/ld+json"
    },
    {
      "@type": "HowToStep",
      "name": "Adicione schema em nível de layout",
      "text": "Coloque schema Organization e WebSite em seu layout.tsx raiz"
    },
    {
      "@type": "HowToStep",
      "name": "Gere schema em nível de página a partir de dados",
      "text": "Construa objetos schema a partir do conteúdo de seu CMS ou banco de dados em cada componente de servidor de página"
    }
  ]
}

Person

Usado em perfis de celebridades de Deluxe Astrology:

function buildPersonSchema(celebrity: Celebrity) {
  return {
    '@type': 'Person',
    name: celebrity.name,
    description: celebrity.bio,
    image: celebrity.photo,
    birthDate: celebrity.birthDate,
    birthPlace: celebrity.birthPlace ? {
      '@type': 'Place',
      name: celebrity.birthPlace,
    } : undefined,
    nationality: celebrity.nationality,
    url: `https://deluxeastrology.com/celebrities/${celebrity.slug}`,
    sameAs: celebrity.externalLinks || [],
  };
}

Schema Dinâmico para Páginas Programáticas

É aqui que fica interessante. Quando você tem 91.000+ páginas apoiadas por linhas do Supabase, você precisa de um pipeline que transforma registros do banco de dados em JSON-LD válido sem intervenção humana.

Aqui está nosso padrão real:

// app/[lang]/horoscope/[sign]/[period]/page.tsx
import { createClient } from '@/lib/supabase/server';
import { JsonLd } from '@/components/json-ld';

export async function generateStaticParams() {
  const supabase = createClient();
  const { data: pages } = await supabase
    .from('horoscope_pages')
    .select('lang, sign, period');

  return (pages || []).map((p) => ({
    lang: p.lang,
    sign: p.sign,
    period: p.period,
  }));
}

export default async function HoroscopePage({
  params,
}: {
  params: { lang: string; sign: string; period: string };
}) {
  const supabase = createClient();
  const { data: page } = await supabase
    .from('horoscope_pages')
    .select('*')
    .eq('lang', params.lang)
    .eq('sign', params.sign)
    .eq('period', params.period)
    .single();

  if (!page) return notFound();

  const articleSchema = {
    '@type': 'Article',
    headline: page.title,
    description: page.meta_description,
    datePublished: page.published_at,
    dateModified: page.updated_at,
    inLanguage: page.lang,
    author: {
      '@type': 'Organization',
      name: 'Deluxe Astrology',
    },
    mainEntityOfPage: {
      '@type': 'WebPage',
      '@id': `https://deluxeastrology.com/${page.lang}/horoscope/${page.sign}/${page.period}`,
    },
  };

  const faqSchema = page.faqs?.length
    ? {
        '@type': 'FAQPage',
        mainEntity: page.faqs.map((faq: any) => ({
          '@type': 'Question',
          name: faq.q,
          acceptedAnswer: {
            '@type': 'Answer',
            text: faq.a,
          },
        })),
      }
    : null;

  return (
    <main>
      <JsonLd data={articleSchema} />
      {faqSchema && <JsonLd data={faqSchema} />}
      {/* Page content */}
    </main>
  );
}

As principais decisões arquitetônicas aqui:

  1. Schema é gerado em tempo de build via SSGgenerateStaticParams cria todos os 91.000+ caminhos, e o schema de cada página é integrado ao HTML estático.
  2. Linha do Supabase = dados de schema — O banco de dados é a única fonte da verdade. Sem desvio de conteúdo entre o que é visível e o que está no schema.
  3. Múltiplos blocos de schema por página — Google suporta explicitamente múltiplas tags script JSON-LD. Usamos blocos separados para Article, FAQPage e BreadcrumbList na mesma página.
  4. ISR para atualização — Definimos revalidate = 3600 para que as páginas sejam reconstruídas a cada hora sem deploys completos.

Para os 25.000 perfis de empresas da HostList, o mesmo padrão se aplica mas com schema Organization gerado de cada linha de empresa do Supabase. Para os 137.000 locais de Not Another Sunday, é LocalBusiness.

Schema Multilíngue Com inLanguage

Deluxe Astrology roda em 30 idiomas. Cada bloco de schema inclui inLanguage, e usamos URLs conscientes de hreflang:

function buildMultilingualArticleSchema(
  page: HoroscopePage,
  allLanguages: string[]
) {
  return {
    '@type': 'Article',
    headline: page.title,
    description: page.meta_description,
    inLanguage: page.lang,
    datePublished: page.published_at,
    dateModified: page.updated_at,
    author: {
      '@type': 'Organization',
      name: 'Deluxe Astrology',
    },
    // Informe aos mecanismos de busca sobre traduções
    workTranslation: allLanguages
      .filter((lang) => lang !== page.lang)
      .map((lang) => ({
        '@type': 'Article',
        inLanguage: lang,
        url: `https://deluxeastrology.com/${lang}/horoscope/${page.sign}/${page.period}`,
      })),
  };
}

A propriedade inLanguage usa tags de idioma BCP 47 (en, fr, de, ja, etc.). Isto é crítico para sites multilíngues — sem isso, Google pode identificar incorretamente o idioma de seus dados estruturados e servi-lo ao público errado.

Ferramentas de Validação e Monitoramento

Entregar schema sem validação é como fazer deploy sem testes. Aqui está nosso toolkit:

Ferramenta Propósito Custo Quando Usar
Google Rich Results Test Valida elegibilidade para rich results Grátis Antes do deploy, verificações pontuais
Schema Markup Validator Validação completa da especificação schema.org Grátis Detecta erros de propriedade que a ferramenta do Google ignora
Screaming Frog Custom Extraction Rastreia site, extrai JSON-LD de cada página £199/ano (licença paga) Validação em massa em 91K+ páginas
Google Search Console Monitora schema indexado, superfícies erros Grátis Monitoramento de produção em andamento
Rich Results Status reports Mostra quais páginas têm schema válido/inválido Grátis Revisão semanal

Screaming Frog Custom Extraction para Schema em Escala

É assim que você valida 91.000 páginas sem verificar manualmente cada uma. Em Screaming Frog:

  1. Vá para Configuration → Custom → Extraction
  2. Adicione uma extração personalizada com CSSPath: script[type="application/ld+json"]
  3. Defina extração para "Extract Inner HTML"
  4. Rastreie seu site
  5. Exporte e analise o JSON para validar programaticamente

Canalizamos a exportação através de um script Node que verifica propriedades obrigatórias por tipo de schema e sinaliza qualquer página com dados ausentes ou malformados. Detecta problemas como campos de headline vazios ou datas no formato errado antes que Google o faça.

Erros Comuns Que Arruinarão Seus Rich Results

Fizemos a maioria destes. Aprenda com nossa dor.

1. O conteúdo do schema não corresponde ao conteúdo visível. Se seu schema Article diz que o headline é "Melhores Restaurantes em Londres" mas o <h1> real diz algo diferente, Google ignorará ou penalizará o schema. Os dados devem refletir o que está na página.

2. Usar tipos de schema para páginas que não se qualificam. Não coloque schema FAQPage em uma página que não exibe realmente conteúdo FAQ. A equipe de ações manuais do Google detecta isso, e a penalidade remove TODOS os seus rich results, não apenas as páginas ofensivas.

3. Propriedades obrigatórias ausentes. Article precisa de headline e image. LocalBusiness precisa de name e address. Verifique a documentação de dados estruturados do Google para requisitos por tipo.

4. Renderizando schema em componentes do cliente. Em Next.js App Router, se você renderizar JSON-LD dentro de um componente 'use client', não estará no HTML inicial. Googlebot geralmente executará JS, mas outros crawlers (incluindo alguns crawlers de LLM) não executarão. Sempre use componentes do servidor.

5. Schema duplicado em layouts aninhados. Se seu layout.tsx raiz e um layout.tsx aninhado renderizarem schema Organization, você terá duplicatas. Deduplicar apenas colocando cada tipo de schema no nível mais específico apropriado.

6. Não escapar caracteres especiais em JSON. Se seu título de artigo ou resposta de FAQ contiver aspas ou colchetes não escapados, o JSON quebra silenciosamente. JSON.stringify() lida com a maioria dos casos, mas fique atento ao conteúdo extraído de dados gerados pelo usuário.

7. Usar tipos de schema descontinuados ou não suportados. Veja a próxima seção.

Descontinuações e Mudanças do Google em 2026

Google tem sido severo sobre quais tipos de schema acionam rich results:

  • Rich results de FAQPage removidos para a maioria dos sites (agosto de 2023, ainda em vigor): Apenas sites de governo e autoridades de saúde obtêm rich results de FAQ em SERPs agora. MAS — e isto é crucial — Google ainda lê e processa schema FAQPage. Simplesmente não mostra a FAQ expansível nos resultados de pesquisa para a maioria dos sites. Para fins de citação por LLM, o schema ainda é ouro.
  • Rich results de HowTo removidos do mobile (setembro de 2023, ainda em vigor): Desktop ainda os mostra ocasionalmente, mas Google tem desprioritizado significativamente os rich results de HowTo.
  • Descontinuação de Sitelinks Searchbox (novembro de 2024): O SearchAction do schema WebSite não garante mais uma caixa de pesquisa de sitelinks, mas Google pode ainda usá-lo internamente.
  • AI Overviews priorizam dados estruturados (2026): Os AI Overviews do Google cada vez mais extraem de páginas com dados estruturados. O schema não garante inclusão, mas páginas sem ele são substancialmente menos prováveis de serem citadas.

Nossa recomendação: continue implementando FAQPage, HowTo e todos os tipos de schema mesmo que os recursos SERP do Google tenham sido reduzidos. Os dados são consumidos por múltiplos sistemas agora — AI do Google, modo browse do ChatGPT, Perplexity, Bing Copilot. O valor se estende muito além de rich results tradicionais.

Se você está construindo um site headless e quer ajuda implementando isso em escala, confira nossas capacidades de desenvolvimento de headless CMS ou entre em contato.

FAQ

O schema FAQPage ainda funciona para SEO em 2026?

Sim, mas de forma diferente que antes. Google removeu rich results de FAQ para a maioria dos sites em 2023, então você não verá snippets de FAQ expandíveis nos resultados de pesquisa. Porém, Google ainda processa o schema internamente, e ferramentas de pesquisa alimentadas por LLM como ChatGPT, Perplexity e AI Overviews do Google extraem ativamente pares de Q&A de marcação FAQPage. Medimos um aumento de 4x em citações por LLM em páginas com schema FAQPage versus aquelas sem.

Como você adiciona marcação schema JSON-LD em Next.js App Router?

Crie um componente de servidor que renderiza uma tag <script type="application/ld+json"> usando dangerouslySetInnerHTML com JSON.stringify() no seu objeto de schema. Coloque dentro do componente de servidor de sua página — nunca em um componente do cliente. Para schema em todo o site como Organization, coloque em layout.tsx. Para schema específico de página como Article ou FAQPage, gere a partir de seus dados em cada page.tsx.

Você pode ter múltiplas tags script JSON-LD em uma página?

Absolutamente. Google suporta explicitamente múltiplos blocos JSON-LD em uma única página. Regularmente renderizamos blocos separados para Article, FAQPage, BreadcrumbList e Organization na mesma página. Cada um recebe sua própria tag <script type="application/ld+json"> com seu próprio @context.

Como você gera marcação de schema para milhares de páginas programáticas?

Construa objetos de schema a partir de linhas do banco de dados em componentes de servidor. Usamos generateStaticParams em Next.js para criar caminhos para todas as páginas, então o componente de servidor de cada página busca seus dados do Supabase e constrói o JSON-LD dinamicamente. O schema é integrado ao HTML estático em tempo de build. Para 91.000 páginas, isto roda durante o processo de build com ISR tratando atualizações.

Qual é a diferença entre schema Article e BlogPosting?

BlogPosting é um subtipo de Article. Use BlogPosting para posts de blog com data de publicação e autor claros. Use Article para conteúdo editorial mais geral como artigos de notícias ou guias. Na prática, Google os trata quase identicamente. Usamos Article para a maioria do conteúdo e BlogPosting apenas para posts explicitamente formatados como blog.

O schema markup ajuda com AI Overviews do Google?

Sim. Páginas com dados estruturados são substancialmente mais prováveis de serem citadas em AI Overviews. O schema ajuda a IA do Google a entender relacionamentos de entidade, tipo de conteúdo e precisão de dados. Schema FAQPage é particularmente eficaz porque fornece pares de Q&A pré-estruturados que a IA pode extrair diretamente. Não é garantia de inclusão, mas melhora significativamente suas chances.

Que ferramentas devo usar para validar marcação de schema em escala?

Para páginas individuais, use o Google Rich Results Test e o Schema Markup Validator em validator.schema.org. Para validação em massa em milhares de páginas, use a funcionalidade de extração personalizada do Screaming Frog para rastrear seu site e extrair todo JSON-LD, depois execute a saída através de um script de validação. Monitore problemas em andamento nos relatórios de dados estruturados do Google Search Console.

Devo implementar tipos de schema para os quais Google não mostra mais rich results?

Sim. Os recursos SERP do Google são apenas um consumidor de seus dados estruturados. ChatGPT, Perplexity, Bing Copilot e outros sistemas de IA leem todos marcação de schema. Mesmo que Google tivesse parado de mostrar rich results de HowTo no mobile, o schema ainda ajuda LLMs a entender seu conteúdo. Pense em dados estruturados como uma camada universal legível por máquina, não apenas um recurso do Google.