Renderizando blocos Gutenberg em React Server Components
Como renderizar HTML do WordPress em Server Components com segurança, com extração automática de FAQ Yoast e schema.org FAQPage.

TL;DR
O editor Gutenberg do WordPress entrega HTML estruturado e semântico via campo content.rendered da REST API. Em vez de reescrever tudo em React, o caminho mais curto e mais robusto é renderizar esse HTML direto em Server Components com dangerouslySetInnerHTML, sanitização confiada ao CMS, e helpers que extraem informações específicas (FAQ, headings, imagens) para enriquecer o output com schema.org automático. Este artigo mostra como fazer com segurança, com regex testada para Yoast FAQ e exemplos de Server Components completos.
content.rendered: o ativo subestimado do WordPress headless
Quando você consome /wp-json/wp/v2/posts, cada post traz um campo content.rendered com o HTML final do post, exatamente como ele apareceria no tema do WordPress. Esse HTML carrega tudo que o editor Gutenberg gerou: cabeçalhos com hierarquia correta, parágrafos, listas, imagens com srcset responsivo, blocos customizados, embeds, tabelas.
A tentação inicial em projetos headless é parsear esse HTML e converter para componentes React, bloco a bloco. É um caminho válido, mas trabalhoso e frágil. Cada bloco custom do WordPress vira um componente para escrever e manter. Atualizações do Gutenberg podem quebrar o parser.
O caminho mais simples e o que a Virtus Design adota em todos os projetos do portfólio: renderizar o content.rendered direto, com dangerouslySetInnerHTML. O HTML já é válido, semântico e otimizado pelo WordPress. Adicionar uma camada de parsing entre WordPress e usuário só introduz pontos de falha.
dangerouslySetInnerHTML: quando é seguro e quando não é
O nome dangerouslySetInnerHTML é um aviso intencional do React: você está injetando HTML diretamente, sem o escape automático que protege contra XSS. Em código de aplicação que recebe input de usuário sem sanitização, isso seria perigoso. Em código que recebe HTML de um CMS controlado pelo seu time, é o uso correto.
A pergunta certa não é “é seguro?”, mas “quem controla o conteúdo que entra aqui?”. Se o WordPress é gerenciado por editores internos e os plugins de sanitização estão ativos (o que é o padrão do WordPress moderno), o HTML que sai da API já é seguro. Renderizar com dangerouslySetInnerHTML apenas reproduz o que o WP renderizaria no tema tradicional.
Em código:
<article
className="post-content"
dangerouslySetInnerHTML={{ __html: post.content.rendered }}
/>
Erro mais comum: permitir editores externos não treinados (clientes ou colaboradores ad hoc) sem revisão, em sites onde o HTML é injetado diretamente. Nesses casos, vale rodar o output por uma biblioteca de sanitização como DOMPurify antes de renderizar. Para times internos com revisão editorial, é desnecessário.
Estilizando o conteúdo do CMS sem Tailwind Typography
Como o HTML vem do WordPress sem classes Tailwind, ele precisa de uma camada CSS dedicada. Em projetos novos com Tailwind v4, o plugin oficial @tailwindcss/typography ainda depende da API antiga (v3) e não está totalmente compatível, conforme já documentamos no nosso workspace interno.
A alternativa que adotamos: criar uma classe .post-content em globals.css com regras específicas para os elementos que o Gutenberg gera. Headings, parágrafos, listas, blockquotes, imagens, tabelas. É menos elegante que prose, mas dá controle total e funciona em qualquer versão do Tailwind:
.post-content h2 { font-size: 1.875rem; margin-top: 2.5rem; }
.post-content p { line-height: 1.75; margin: 1rem 0; }
.post-content ul { padding-left: 1.5rem; }
.post-content blockquote { border-left: 3px solid var(--cs-neon); }
.post-content img { border-radius: 1rem; }
Esse CSS aplica automaticamente ao conteúdo do CMS sem que cada componente precise se preocupar. Como o WordPress já gera headings em hierarquia correta (H2, H3, H4) e parágrafos limpos, a estilização é direta.
Extraindo blocos Yoast FAQ via regex (com FAQPage automático)
O plugin Yoast SEO traz um bloco Gutenberg específico para perguntas frequentes. Quando o editor adiciona esse bloco no post, o HTML renderizado contém uma estrutura previsível com classes schema-faq-section, schema-faq-question e schema-faq-answer.
Em vez de parsear o JSON inteiro do bloco, é mais simples extrair os pares pergunta/resposta diretamente do HTML com regex e usá-los para emitir o JSON-LD FAQPage esperado pelo Google. Esse schema desbloqueia rich results na SERP, com perguntas e respostas expandíveis abaixo do título.
O helper que usamos:
export function extractFaqsFromContent(html: string) {
if (!html.includes("schema-faq-section")) return [];
const faqs = [];
const sectionRe = /<div[^>]*class="[^"]*schema-faq-section[^"]*"[^>]*>([\s\S]*?)<\/div>/gi;
// ... extrai question e answer e empilha
return faqs;
}
O componente da página chama o helper, mapeia as FAQs e emite um script type="application/ld+json" com schema.org FAQPage diretamente no <head>. O resultado: páginas com FAQ ganham rich results sem que o time editorial precise saber o que é JSON-LD.
Custom blocks: Server Components com hidratação seletiva
Em alguns projetos, vale a pena criar componentes React específicos para blocos especiais (galerias com lightbox, calculadoras embutidas, formulários integrados ao HubSpot). Nesses casos, o WordPress emite um marcador HTML específico (uma div com classe identificável) e o front-end substitui esse marcador pelo componente React no momento da renderização.
O padrão que funciona bem com Server Components: parsear o HTML do post procurando por marcadores conhecidos, e dividir o conteúdo em segmentos. Cada segmento ou é HTML cru (renderizado com dangerouslySetInnerHTML) ou é um componente React específico (renderizado normalmente).
Checklist: quando criar um custom block
-
O bloco precisa de interatividade que HTML estático não entrega (modal, slider, formulário com validação)
-
O bloco aparece em vários posts e justifica a manutenção de um componente dedicado
-
O bloco precisa de dados externos no momento da renderização (cotação, estoque, disponibilidade)
-
A acessibilidade ou a performance do bloco padrão do Gutenberg não atende
Para o resto, o HTML do content.rendered resolve sem custo adicional.
Helpers de limpeza: stripHtml e fixContentUrls
Mesmo renderizando o HTML direto, alguns helpers de limpeza são essenciais. Os dois que aparecem em todos os projetos do nosso portfólio:
stripHtml(html: string): remove todas as tags HTML e retorna texto puro. É usado em meta descriptions (que não aceitam HTML), em previews de cards, em alt text dinâmico:
export function stripHtml(html: string): string {
return html.replace(/<[^>]*>/g, "").trim();
}
fixContentUrls(html: string): usado em projetos que migraram de domínio. Se o WordPress originalmente estava em antigo.com.br/wp-content/... e migrou para wp.cliente.com.br/wp-content/..., o conteúdo legado pode ter URLs hardcoded apontando para o domínio antigo. O helper substitui todas as ocorrências por uma busca-e-substitui simples no momento da renderização.
Em projetos novos sem migração, o segundo não é necessário. Mas é bom saber que existe, porque resolve um problema real e silencioso.
Edge cases: emojis, embeds, tabelas
O HTML do WordPress eventualmente traz surpresas. Três casos que merecem atenção:
Emojis: o WordPress por padrão converte emojis Unicode em imagens hospedadas no s.w.org. Em sites Next.js modernos, isso é desnecessário (todos os navegadores renderizam emojis nativos). Vale desativar o plugin de emojis no painel para que o HTML retorne com Unicode puro.
Embeds (YouTube, Twitter, Vimeo): o WordPress gera iframes com URLs do oEmbed. Eles funcionam, mas carregam scripts externos que podem impactar Core Web Vitals. Para vídeos críticos do hero, vale substituir por componente React lazy. Para vídeos secundários no corpo do post, deixar como está costuma ser aceitável.
Tabelas: o bloco de tabela do Gutenberg gera HTML semântico (<table>, <thead>, <tbody>). Estilizar com CSS responsivo no .post-content resolve. Em mobile, considerar overflow-x: auto no container para evitar quebra de layout.
Próximos passos
Renderizar blocos Gutenberg em React Server Components é um dos pontos onde a simplicidade vence a sofisticação. Confiar no HTML do WordPress, complementar com helpers específicos e emitir schema.org automático entrega o melhor resultado com o menor custo de manutenção.
Os próximos artigos da série tratam de outras camadas críticas: otimização de Core Web Vitals e SEO programático no Next.js 16. Para entender a arquitetura completa por trás desta camada, leia o artigo sobre REST API, ISR e Application Passwords. E se você ainda está avaliando se vale migrar, comece pelo artigo de visão estratégica.
Implementar este padrão no seu projeto? Fale com a Virtus Design. Conheça também o portfólio de serviços, do desenvolvimento ao SEO técnico.

