HomeBlog

Como Implementar técnicas de Seo com Nextjs

Descubra como eu implemento SEO técnico no Next.js de forma prática e descomplicada. Aprenda a criar metadata dinâmica, configurar robots.ts e sitemap.ts, usar JSON-LD para rich snippets, e aplicar HTML semântico — tudo com exemplos reais de código. Um guia completo para desenvolvedores que querem ver seus projetos ranquearem no Google sem complicação.

Ewerton FS

Ewerton FS

02 de dezembro, 2025

Como Implementar técnicas de Seo com Nextjs

Por Que Nos Esforçarmos para Ter um Bom SEO? Uma Conversa Sincera

Sabe, como desenvolvedor, muitas vezes focamos tanto na performance do código, na arquitetura e na experiência do usuário (o que é excelente!) que o SEO (Search Engine Optimization) acaba ficando em segundo plano. Poxa, o Next.js já faz tanta coisa "mágica" para otimização! Mas a verdade é que, para que o nosso trabalho alcance quem realmente precisa dele – seja um usuário, um potencial cliente ou um recrutador – precisamos dar uma ajudinha extra aos mecanismos de busca, como o Google.

Ter um bom SEO não é sobre "enganar" o algoritmo; é sobre comunicação clara. É garantir que o Google, o Bing e outros entendam exatamente o valor do conteúdo que você criou, facilitando a vida deles (e a nossa!). E o melhor: se você tem um blog pessoal, um projeto open-source ou um e-commerce, um bom ranqueamento é a diferença entre ser encontrado e ficar perdido no vasto oceano da internet.

O Next.js é um framework fantástico que nos dá uma base sólida de otimizações de performance. Abaixo, compartilho oito passos essenciais e práticos que eu utilizo e que, baseados em pesquisas e na minha própria experiência, farão seu website brilhar nos resultados de busca. Vamos juntos!

As 8 Chaves de SEO que Todo Dev Next.js Deve Conhecer

1. Primeiro, Utilize Rotas Server-Renderizadas ou Estáticas (SSR/SSG)

Sei que pode parecer básico, mas é crucial: para o SEO, o ideal é que o conteúdo da sua página já esteja pronto no HTML quando o crawler do Google chegar. O Next.js nos facilita a vida com o Server-Side Rendering (SSR) e a Static Site Generation (SSG).

  • Por que é importante? Páginas renderizadas totalmente no cliente (Client-Side Rendering - CSR), onde o conteúdo é injetado depois via JavaScript, são mais difíceis de serem totalmente indexadas. SSR e SSG garantem que o conteúdo principal (textos, títulos) esteja visível e lido instantaneamente pelos motores de busca.

Exemplo Prático - Gerando páginas estáticas para posts do blog:

TypeScript

// app/blog/[slug]/page.tsx// Esta função gera todas as rotas estáticas em build timeexport async function generateStaticParams() { const posts = await getAllPosts(); return posts.map((post) => ({ slug: post.slug })); }

2. Crie um Objeto metadata Global e por Rota

No Next.js moderno (App Router), a criação de metadata ficou absurdamente simples. Você pode criar um objeto metadata em arquivos layout.tsx (para ser global) ou page.tsx (para ser específico da rota).

  • O que ele deve ter? Título (title), descrição (description), e principalmente o Open Graph (openGraph) e o Twitter Card (twitter).

  • A dica de ouro: Olhe o site OpenGraph! O openGraph é o que faz sua pré-visualização ficar linda quando você compartilha o link no WhatsApp, LinkedIn ou Instagram. Isso não é diretamente SEO para ranqueamento, mas é Neuromarketing puro: aumenta a taxa de clique e a credibilidade!

Exemplo Prático - Metadata global (app/layout.tsx):

TypeScript

import type { Metadata } from 'next'; export const metadata: Metadata = { metadataBase: new URL('https://seusite.com.br'), title: { default: 'Seu Site - Título Principal', template: '%s | Seu Site' }, description: 'Descrição do seu site que aparecerá nos resultados do Google', keywords: ['palavra-chave 1', 'palavra-chave 2', 'palavra-chave 3'], authors: [{ name: 'Seu Nome' }], creator: 'Seu Nome', publisher: 'Sua Empresa', // Open Graph para redes sociaisopenGraph: { type: 'website', locale: 'pt_BR', url: 'https://seusite.com.br', siteName: 'Seu Site', title: 'Seu Site - Onde a Qualidade Encontra a Excelência', description: 'Descrição atraente para redes sociais', images: [ { url: '/og-image.jpg', width: 1200, height: 630, alt: 'Seu Site', }, ], }, // Twitter Cardtwitter: { card: 'summary_large_image', title: 'Seu Site', description: 'Descrição para Twitter', images: ['/og-image.jpg'], creator: '@seutwitter', }, // Configurações de robotsrobots: { index: true, follow: true, googleBot: { index: true, follow: true, }, }, // URL canônicaalternates: { canonical: 'https://seusite.com.br', }, };

Exemplo Prático - Metadata específico de uma página (app/servicos/page.tsx):

TypeScript

import type { Metadata } from 'next'; export const metadata: Metadata = { title: 'Nossos Serviços', description: 'Conheça todos os nossos serviços de alta qualidade', keywords: ['serviço 1', 'serviço 2', 'serviço 3'], alternates: { canonical: 'https://seusite.com.br/servicos', }, openGraph: { title: 'Nossos Serviços | Seu Site', description: 'Conheça todos os nossos serviços', url: 'https://seusite.com.br/servicos', images: [{ url: '/servicos-og.jpg' }], }, };

3. E se a Página for Dinâmica? A Geração de Metadata é Dinâmica?

É totalmente possível e necessário criar metadata única, mesmo para páginas dinâmicas, como /blog/[slug].

  • Como fazer? Você pode utilizar a função assíncrona generateMetadata no seu arquivo page.tsx da rota dinâmica. Você fará uma busca dos dados (por exemplo, o título e a descrição do artigo) e retornará um objeto metadata personalizado.

  • O resultado: Cada artigo do seu blog terá um título e uma descrição únicos e otimizados, o que o Google ama, pois demonstra conteúdo de qualidade e relevância específica.

Exemplo Prático - Metadata dinâmica para posts do blog:

TypeScript

// app/blog/[slug]/page.tsximport type { Metadata } from 'next'; import { getPostBySlug } from '@/lib/data'; interface PageProps { params: Promise<{ slug: string }>; } export async function generateMetadata({ params }: PageProps): Promise<Metadata> { const { slug } = await params; const post = await getPostBySlug(slug); if (!post) { return { title: 'Post não encontrado' }; } const postUrl = `https://seusite.com.br/blog/${slug}`; // Gerar keywords dinamicamente baseadas no título e categoriaconst generateKeywords = (post): string[] => { const baseKeywords = [ 'Seu Site', 'blog', post.category ? post.category.toLowerCase() : 'geral', ]; const titleWords = post.title .toLowerCase() .split(/\s+/) .filter(word => word.length > 3); return [...baseKeywords, ...titleWords.slice(0, 5)]; }; return { title: post.title, description: post.subtitle || post.title, keywords: generateKeywords(post), authors: [{ name: post.author.name }], creator: post.author.name, publisher: 'Seu Site', category: post.category, openGraph: { title: post.title, description: post.subtitle || post.title, url: postUrl, siteName: 'Seu Site - Blog', images: [ { url: post.image.url, width: 1200, height: 630, alt: post.title, }, ], locale: 'pt_BR', type: 'article', publishedTime: post.createdAt, modifiedTime: post.createdAt, authors: [post.author.name], section: post.category, tags: generateKeywords(post), }, twitter: { card: 'summary_large_image', title: post.title, description: post.subtitle || post.title, images: [post.image.url], creator: '@seutwitter', }, alternates: { canonical: postUrl, }, }; }

4. Crie um robots.ts para Orientar os Crawlers

No Next.js (com App Router), o arquivo robots.txt é substituído pelo robots.ts. Nele, você pode dizer aos mecanismos de busca o que eles devem ou não varrer e indexar.

  • O poder do robots.ts: É onde você pode usar a diretiva User-agent: * e Disallow: /admin para bloquear páginas privadas, ou usar a diretiva Allow para áreas importantes. Você controla o fluxo de indexação.

Exemplo Prático - app/robots.ts:

TypeScript

import { MetadataRoute } from 'next'; export default function robots(): MetadataRoute.Robots { const baseUrl = 'https://seusite.com.br'; return { rules: [ { userAgent: '*', allow: '/', disallow: [ '/api/', // Bloquear APIs'/_next/', // Bloquear arquivos internos do Next.js'/admin/', // Bloquear área administrativa ], }, { userAgent: 'Googlebot', allow: '/', crawlDelay: 0, // Google pode rastrear sem atraso }, { userAgent: 'Bingbot', allow: '/', crawlDelay: 0, }, ], sitemap: `${baseUrl}/sitemap.xml`, }; }

5. Crie um sitemap.ts para Mapear Seu Conteúdo

O Sitemap é, basicamente, um mapa rodoviário para os mecanismos de busca. Ele lista todas as URLs do seu site que você quer que sejam indexadas.

  • Por que devo criar? Ele garante que o Google não perca nenhuma página importante do seu projeto, especialmente se ela estiver em um nível mais profundo de navegação ou se o seu site for muito novo. O Next.js permite que você gere isso dinamicamente usando o sitemap.ts, garantindo que novas rotas sejam incluídas automaticamente.

Exemplo Prático - app/sitemap.ts:

TypeScript

import { MetadataRoute } from 'next'; import { getAllPosts } from '@/lib/data'; export default async function sitemap(): Promise<MetadataRoute.Sitemap> { const baseUrl = 'https://seusite.com.br'; // Páginas estáticasconst staticRoutes = [ '', // Home'/servicos', '/sobre', '/contato', ]; const staticPages = staticRoutes.map((route) => ({ url: `${baseUrl}${route}`, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: route === '' ? 1 : 0.8, })); // Posts do Blog (Dinâmico)const posts = await getAllPosts(); const blogPages = posts.map((post) => ({ url: `${baseUrl}/blog/${post.slug}`, lastModified: new Date(post.createdAt), changeFrequency: 'weekly' as const, priority: 0.7, })); return [...staticPages, ...blogPages]; }

6. Otimize Imagens e Utilize o Componente Image do Next.js

A performance é um fator de ranqueamento crucial! Imagens pesadas e não otimizadas podem derrubar a velocidade da sua página.

  • A alternativa ao metadata: Utilize o componente nativo next/image! Ele otimiza, redimensiona e serve as imagens em formatos modernos (como WebP) automaticamente.

  • O toque de SEO: Não se esqueça do atributo alt (texto alternativo) para descrever a imagem. É vital para a acessibilidade e para o Google entender o contexto visual da sua página.

Exemplo Prático - Uso correto do componente Image:

TypeScript

import Image from 'next/image'; export default function MeuComponente() { return ( <div> {/* Imagem hero - prioridade alta */} <Image src="/hero-image.jpg" alt="Descrição detalhada da imagem para SEO" width={1200} height={630} priority // Carrega imediatamente className="rounded-lg object-cover" /> {/* Imagem de conteúdo - lazy loading */} <Image src="/content-image.jpg" alt="Outra descrição rica em palavras-chave" width={800} height={400} loading="lazy" // Carrega quando estiver visível className="rounded-lg" /> </div> ); }

7. Utilize JSON-LD (Schema.org) para Conteúdo Estruturado

Aqui entramos na nata do SEO técnico! O JSON-LD (utilizando vocabulário do Schema.org) permite que você marque seu conteúdo para que o Google entenda seu significado, e não apenas o texto.

  • Exemplo prático: Se o seu artigo for uma receita, você pode usar o tipo Recipe. Se for um artigo de blog, o tipo Article.

  • O benefício: Isso pode gerar os famosos "Rich Snippets" (aquelas caixas de destaque com estrelas, fotos ou respostas diretas) nos resultados de pesquisa. É a forma mais poderosa de se destacar!

Exemplo Prático - Structured Data para BlogPosting:

TypeScript

// Dentro do componente da página do blog export default async function PostPage({ params }: PageProps) { const { slug } = await params; const post = await getPostBySlug(slug); return ( <main> <article> <header> <h1>{post.title}</h1> {/* Script JSON-LD para Rich Snippets */} <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({ "@context": "https://schema.org", "@type": "BlogPosting", headline: post.title, description: post.subtitle || post.title, image: [post.image.url], datePublished: post.createdAt, dateModified: post.createdAt, author: [ { "@type": "Person", name: post.author.name, url: "https://seusite.com.br", }, ], publisher: { "@type": "Organization", name: "Seu Site", logo: { "@type": "ImageObject", url: "https://seusite.com.br/logo.png", }, }, mainEntityOfPage: { "@type": "WebPage", "@id": `https://seusite.com.br/blog/${slug}`, }, }), }} /> </header> <div dangerouslySetInnerHTML={{ __html: post.content.html }} /> </article> </main> ); }

8. Utilize HTML Semântico: O Alicerce da Web

Não é só o Next.js; é a base da Web. Utilize as tags HTML da forma como foram desenhadas.

  • Por que o HTML Semântico importa? O Google interpreta o significado das tags: <h1> é o título mais importante da página, <nav> é o menu de navegação, <article> é o conteúdo principal.

  • Dica do blog: Se você tem um blog, use a tag <article> para envolver o conteúdo principal do post e <section> para dividir tópicos. Isso ajuda os crawlers a estruturar a hierarquia do seu conteúdo de forma clara.

Exemplo Prático - Estrutura semântica de um post de blog:

TypeScript

export default async function PostPage({ params }: PageProps) { const { slug } = await params; const post = await getPostBySlug(slug); return ( <main className="mx-auto max-w-3xl"> <article> {/* Header do artigo */} <header className="mb-8 text-center"> <h1 className="text-4xl font-bold"> {post.title} </h1> {/* Metadados do post */} <div className="mt-4 flex items-center gap-4"> <img src={post.author.image.url} alt={post.author.name} className="rounded-full" /> <div> <p>{post.author.name}</p> <time dateTime={post.createdAt}> {new Date(post.createdAt).toLocaleDateString('pt-BR')} </time> </div> </div> </header> {/* Imagem principal */} <div className="mb-8"> <Image src={post.image.url} alt={post.title} width={1200} height={630} className="rounded-lg" /> </div> {/* Conteúdo do artigo */} <div className="prose prose-lg" dangerouslySetInnerHTML={{ __html: post.content.html }} /> </article> {/* Footer do post */} <footer className="mt-12 border-t pt-6"> <a href="/" className="text-primary hover:underline"> ← Voltar para o blog </a> </footer> </main> ); }

Tags Semânticas Importantes:

  • <main>: //Conteúdo principal da página
  • <article>: //Conteúdo independente (um post, um produto)
  • <header>:// Cabeçalho do artigo com título e metadados
  • <h1>: //Título principal (apenas UM por página!)
  • <time>: //Data de publicação
  • <footer>: //Rodapé do artigo

Conclusão: SEO é Cuidado, Não Complicação

O SEO pode parecer um bicho de sete cabeças, mas, como desenvolvedores, já temos a ferramenta certa nas mãos: o Next.js.

Com essas oito dicas práticas – que vão desde a renderização até a marcação avançada com Schema.org – você não apenas melhorará o ranqueamento, mas também a qualidade e a acessibilidade do seu projeto.

Em Resumo:

  • Use SSG/SSR para conteúdo importante

  • Configure metadata global E específico

  • Crie robots.ts e sitemap.ts

  • Otimize imagens com next/image

  • Use JSON-LD para rich snippets

  • Escreva HTML semântico

É um esforço pequeno, mas com um retorno imenso. Mantenha a curiosidade e continue aprimorando!

Recursos Úteis: