seo·8 min de lectura

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 dinamico, 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 mas. Combinado con sitemap dinamico y structured data, tienes todo lo que Google necesita.

Metadata API basica

Exporta un objeto metadata desde cualquier page.tsx o layout.tsx:

typescript
// app/page.tsx
import type { Metadata } from "next";
 
export const metadata: Metadata = {
  title: "Mi App - Inicio",
  description: "Descripcion que aparece en Google. 150-160 caracteres ideal.",
  keywords: ["nextjs", "react", "typescript"],
};

Next.js genera automaticamente:

html
<title>Mi App - Inicio</title>
<meta name="description" content="Descripcion que aparece en Google..." />
<meta name="keywords" content="nextjs, react, typescript" />

Metadata dinamica (paginas con [slug])

Para paginas dinamicas como posts de blog:

typescript
// 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 titulo

Define un template en el layout root para no repetir el nombre del sitio:

typescript
// 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 como se ve tu pagina al compartirla:

typescript
export const metadata: Metadata = {
  openGraph: {
    type: "website",
    title: "Mi App",
    description: "Descripcion 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: "Descripcion 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 dinamico

typescript
// app/sitemap.ts
import type { MetadataRoute } from "next";
 
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts(); // tu funcion 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 automaticamente en /sitemap.xml. Para mas detalles, revisa la guia de sitemap automatico en Next.js.

robots.txt

typescript
// 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 mas utiles:

typescript
// 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 */}
  </>
);
typescript
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 mas probabilidad de aparecer en "People Also Ask" de Google.

Checklist de SEO

ElementoDondeVerificar
Title unicoCada page.tsx< 60 caracteres
Meta descriptionCada page.tsx150-160 caracteres
Open Graph imagePaginas importantes1200x630px
Canonical URLPaginas con contenido duplicadoalternates.canonical
Sitemap/sitemap.xmlTodas las paginas incluidas
robots.txt/robots.txtNo bloquea paginas importantes
JSON-LDPosts del blogBlogPosting + FAQPage
HeadingsContenidoH1 unico, H2/H3 jerarquicos

Siguiente paso

El SEO tecnico es la base. Para que tu contenido rankee, necesitas contenido de calidad con buena estructura. Si deployeas en Vercel, la guia de deploy en Vercel cubre la configuracion completa incluyendo dominios y analytics.

#seo#nextjs#metadata#opengraph#sitemap#structured-data

Preguntas frecuentes

Como agrego meta tags en Next.js?

Con la Metadata API. Exportas un objeto metadata o una funcion generateMetadata desde tu page.tsx o layout.tsx. Next.js genera automaticamente los meta tags en el HTML.

Como genero un sitemap dinamico en Next.js?

Crea un archivo app/sitemap.ts que exporte una funcion. Esa funcion lee tus paginas (de la base de datos, del filesystem, etc.) y devuelve un array con las URLs. Next.js lo sirve como XML en /sitemap.xml automaticamente.

Que es Open Graph y por que importa?

Open Graph son meta tags que controlan como se ve tu pagina cuando alguien la comparte en redes sociales. Sin ellos, Twitter/LinkedIn/WhatsApp muestran un preview generico. Con ellos, muestran tu titulo, descripcion 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, articulos). Para un blog, BlogPosting y FAQPage son los schemas mas utiles.