SEO en Next.js: Metadata API, Open Graph y Sitemap
Optimiza el SEO de tu app Next.js. Metadata API, Open Graph, Twitter Cards, sitemap dinámico, robots.txt y structured data con JSON-LD.
SEO en Next.js: Metadata API, Open Graph y Sitemap
El SEO en Next.js ya no requiere paquetes externos como next-seo. El App Router tiene una Metadata API nativa que genera meta tags, Open Graph, Twitter Cards y más. Combinado con sitemap dinámico y structured data, tienes todo lo que Google necesita.
Metadata API básica
Exporta un objeto metadata desde cualquier page.tsx o layout.tsx:
// app/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Mi App - Inicio",
description: "Descripción que aparece en Google. 150-160 caracteres ideal.",
keywords: ["nextjs", "react", "typescript"],
};Next.js genera automáticamente:
<title>Mi App - Inicio</title>
<meta name="description" content="Descripción que aparece en Google..." />
<meta name="keywords" content="nextjs, react, typescript" />Metadata dinámica (páginas con [slug])
Para páginas dinámicas como posts de blog:
// app/blog/[slug]/page.tsx
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,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
type: "article",
publishedTime: post.publishedAt,
images: [{ url: post.image, width: 1200, height: 630 }],
},
};
}Template de título
Define un template en el layout root para no repetir el nombre del sitio:
// app/layout.tsx
export const metadata: Metadata = {
title: {
default: "Mi App",
template: "%s | Mi App", // "Sobre Mi | Mi App"
},
};Open Graph y Twitter Cards
Controla cómo se ve tu página al compartirla:
export const metadata: Metadata = {
openGraph: {
type: "website",
title: "Mi App",
description: "Descripción para redes sociales",
url: "https://miapp.com",
siteName: "Mi App",
locale: "es_ES",
images: [{
url: "https://miapp.com/og-image.jpg",
width: 1200,
height: 630,
alt: "Preview de Mi App",
}],
},
twitter: {
card: "summary_large_image",
title: "Mi App",
description: "Descripción para Twitter",
images: ["https://miapp.com/og-image.jpg"],
},
};La imagen OG debe ser de 1200x630 px. Es lo que aparece como preview en Twitter, LinkedIn, WhatsApp y Slack.
Sitemap dinámico
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts(); // tu función para obtener posts
const blogUrls = posts.map((post) => ({
url: `https://miapp.com/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: "monthly" as const,
priority: 0.8,
}));
return [
{
url: "https://miapp.com",
lastModified: new Date(),
changeFrequency: "weekly",
priority: 1,
},
{
url: "https://miapp.com/blog",
lastModified: new Date(),
changeFrequency: "weekly",
priority: 0.9,
},
...blogUrls,
];
}Next.js lo sirve automáticamente en /sitemap.xml. Para más detalles, revisa la guia de sitemap automatico en Next.js.
robots.txt
// app/robots.ts
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: "*",
allow: "/",
disallow: ["/api/", "/admin/"],
},
],
sitemap: "https://miapp.com/sitemap.xml",
};
}Structured Data (JSON-LD)
Google usa structured data para rich results. Para un blog, BlogPosting y FAQPage son los más útiles:
// En tu page.tsx
const articleSchema = {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.title,
description: post.description,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: {
"@type": "Person",
name: "Rod Alexanderson",
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(articleSchema),
}}
/>
{/* Tu contenido */}
</>
);FAQPage para featured snippets
const faqSchema = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((faq) => ({
"@type": "Question",
name: faq.question,
acceptedAnswer: {
"@type": "Answer",
text: faq.answer,
},
})),
};Las FAQs con structured data tienen más probabilidad de aparecer en "People Also Ask" de Google.
Checklist de SEO
| Elemento | Donde | Verificar |
|---|---|---|
| Title único | Cada page.tsx | < 60 caracteres |
| Meta description | Cada page.tsx | 150-160 caracteres |
| Open Graph image | páginas importantes | 1200x630px |
| Canonical URL | páginas con contenido duplicado | alternates.canonical |
| Sitemap | /sitemap.xml | Todas las páginas incluidas |
| robots.txt | /robots.txt | No bloquea páginas importantes |
| JSON-LD | Posts del blog | BlogPosting + FAQPage |
| Headings | Contenido | H1 único, H2/H3 jerarquicos |
Siguiente paso
El SEO técnico es la base. Para que tu contenido rankee, necesitas contenido de calidad con buena estructura. Si deployeas en Vercel, la guía de deploy en Vercel cubre la configuración completa incluyendo dominios y analytics.
Preguntas frecuentes
¿Cómo agrego meta tags en Next.js?
Con la Metadata API. Exportas un objeto metadata o una función generateMetadata desde tu page.tsx o layout.tsx. Next.js genera automáticamente los meta tags en el HTML.
¿Cómo genero un sitemap dinámico en Next.js?
Crea un archivo app/sitemap.ts que exporte una función. Esa función lee tus páginas (de la base de datos, del filesystem, etc.) y devuelve un array con las URLs. Next.js lo sirve como XML en /sitemap.xml automáticamente.
¿Qué es Open Graph y por qué importa?
Open Graph son meta tags que controlan cómo se ve tu página cuando alguien la comparte en redes sociales. Sin ellos, Twitter/LinkedIn/WhatsApp muestran un preview genérico. Con ellos, muestran tu título, descripción e imagen custom.
¿Necesito structured data (JSON-LD) en Next.js?
No es obligatorio, pero ayuda. Google usa structured data para entender mejor tu contenido y mostrarlo en rich results (FAQ, breadcrumbs, artículos). Para un blog, BlogPosting y FAQPage son los schemas más útiles.
Articulos relacionados
12 Errores SEO que Arruinan tu Blog (y Como Arreglarlos)
Los errores SEO mas comunes que destrozan blogs de desarrolladores. Diagnostico, fix y verificacion para cada uno. Para blogs en Next.js y cualquier stack.
Indexar tu Blog en Google: Guia Tecnica
Como indexar un blog en Google Search Console: sitemap, robots.txt, canonical URLs, structured data, Core Web Vitals e IndexNow. Guia tecnica completa.
IndexNow en Next.js: guía completa para indexación rápida
Implementa IndexNow en tu app Next.js con TypeScript. Aprende a generar tu API key, enviar URLs a Bing automáticamente y configurar el webhook de Vercel.