seo-metadata-patterns
Implements SEO, metadata, and structured data for Next.js applications.
Install
mkdir -p .claude/skills/seo-metadata-patterns && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14348" && unzip -o skill.zip -d .claude/skills/seo-metadata-patterns && rm skill.zipInstalls to .claude/skills/seo-metadata-patterns
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Complete SEO patterns for Next.js — Metadata API, Open Graph, Twitter cards, JSON-LD structured data, sitemap, and robots.txt. Use when shipping any public-facing page.Key capabilities
- →Configure static metadata for Next.js pages
- →Set root layout metadata for global defaults
- →Override page-level titles dynamically
- →Generate dynamic metadata for blog posts or products
- →Implement JSON-LD structured data for various schemas
How it works
The skill provides patterns for configuring Next.js Metadata API, Open Graph, Twitter cards, JSON-LD structured data, sitemap, and robots.txt. It guides the user through setting static and dynamic metadata, and generating necessary SEO files.
Inputs & outputs
When to use seo-metadata-patterns
- →Setting up metadata for public pages
- →Adding JSON-LD structured data
- →Generating robots.txt and sitemaps
About this skill
SEO & Metadata Patterns
Purpose
Ensure every public-facing page has proper SEO metadata, social sharing cards, and structured data. These patterns work with Next.js App Router's Metadata API.
When to Use This Skill
- Shipping any page that will be publicly accessible
- Setting up social sharing (OG images, Twitter cards)
- Adding structured data for rich search results
- Generating sitemaps and robots.txt
Output Format / Delivery
Provide highly optimized, crawlable, and fully compliant SEO metadata, Open Graph configs, sitemap generators, and JSON-LD structured schemas.
Behavior Rules
- Never use generic or duplicate metadata descriptions across distinct routes — always tail-cut descriptions to 120-160 characters.
- Always define metadataBase in layout.tsx before exporting dynamic relative URLs.
- Never allow indexable staging or admin environments — always exclude them in
robots.tsor withnoindexheaders.
Maintenance Notes
This skill is locked for standard Next.js App Router metadata features. Update if Next.js introduces new Metadata API abstractions.
Phase 1: Metadata API (Next.js)
Static Metadata (Most Pages)
// app/page.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Product Name — Clear Value Proposition",
description: "A compelling description (120-160 chars) that accurately describes the page content and includes primary keywords naturally.",
keywords: ["saas", "product", "solution"],
openGraph: {
title: "Product Name — Clear Value Proposition",
description: "OG description for social sharing. Can differ from meta description.",
url: "https://product.com",
siteName: "Product Name",
locale: "en_US",
type: "website",
images: [{
url: "https://product.com/og-image.png",
width: 1200,
height: 630,
alt: "Product Name — descriptive alt text",
}],
},
twitter: {
card: "summary_large_image",
title: "Product Name — Clear Value Proposition",
description: "Twitter card description.",
images: ["https://product.com/og-image.png"],
creator: "@handle",
},
robots: { index: true, follow: true },
alternates: {
canonical: "https://product.com",
},
};
Root Layout Metadata (Global Defaults)
// app/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
metadataBase: new URL("https://product.com"),
title: {
template: "%s — Product Name", // Dynamic: page title replaces %s
default: "Product Name", // Fallback
},
description: "Default description for pages without their own.",
};
Page-Level Title Override
// app/pricing/page.tsx
export const metadata = {
title: "Pricing", // Renders as "Pricing — Product Name" via template
};
Phase 2: Dynamic Metadata (Blog Posts, Products)
// app/blog/[slug]/page.tsx
import type { Metadata } from "next";
type Props = { params: Promise<{ slug: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return { title: "Not Found" };
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: "article",
publishedTime: post.publishedAt,
authors: [post.author.name],
images: [{
url: post.coverImage,
width: 1200,
height: 630,
alt: post.title,
}],
},
};
}
Phase 3: JSON-LD Structured Data
Organization Schema (Root Layout)
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
const jsonLd = {
"@context": "https://schema.org",
"@type": "Organization",
name: "Product Name",
url: "https://product.com",
logo: "https://product.com/logo.png",
sameAs: [
"https://twitter.com/product",
"https://github.com/product",
],
};
return (
<html lang="en">
<body>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{children}
</body>
</html>
);
}
Product Schema (Pricing Page)
const jsonLd = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
name: "Product Name",
applicationCategory: "BusinessApplication",
offers: {
"@type": "AggregateOffer",
lowPrice: "29",
highPrice: "199",
priceCurrency: "USD",
offerCount: "3",
},
aggregateRating: {
"@type": "AggregateRating",
ratingValue: "4.8",
reviewCount: "2847",
},
};
Article Schema (Blog Posts)
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
description: post.excerpt,
image: post.coverImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: {
"@type": "Person",
name: post.author.name,
},
publisher: {
"@type": "Organization",
name: "Product Name",
logo: { "@type": "ImageObject", url: "https://product.com/logo.png" },
},
};
Phase 4: Sitemap Generation
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts();
const staticRoutes: MetadataRoute.Sitemap = [
{ url: "https://product.com", lastModified: new Date(), changeFrequency: "weekly", priority: 1 },
{ url: "https://product.com/pricing", lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
{ url: "https://product.com/about", lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 },
];
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => ({
url: `https://product.com/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: "weekly" as const,
priority: 0.6,
}));
return [...staticRoutes, ...blogRoutes];
}
Phase 5: Robots.txt
// app/robots.ts
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: "*",
allow: "/",
disallow: ["/api/", "/dashboard/", "/_next/"],
},
],
sitemap: "https://product.com/sitemap.xml",
};
}
Phase 6: SEO Checklist
Per-Page Requirements
-
<title>is unique, descriptive, 50-60 characters -
<meta description>is unique, compelling, 120-160 characters - One
<h1>per page, matches the page topic - Heading hierarchy is logical (h1 → h2 → h3, no skips)
- Images have descriptive
alttext - Internal links use meaningful anchor text (not "click here")
-
canonicalURL is set to prevent duplicate content - OG image is 1200×630px with readable text at small sizes
Site-Wide Requirements
-
sitemap.xmlis generated and includes all public routes -
robots.txtexists and allows crawling of public pages - JSON-LD Organization schema on root layout
- All pages have unique titles (no duplicates)
- No orphan pages (every page is linked from at least one other page)
- Page load time < 3s on mobile (LCP)
When not to use it
- →When using generic or duplicate metadata descriptions across distinct routes
- →When not defining `metadataBase` in `layout.tsx` before exporting dynamic relative URLs
- →When allowing indexable staging or admin environments
Limitations
- →Never use generic or duplicate metadata descriptions across distinct routes
- →Always define `metadataBase` in `layout.tsx`
- →Never allow indexable staging or admin environments
How it compares
This skill offers a complete set of SEO patterns specifically for Next.js App Router's Metadata API, providing a structured and compliant approach to metadata management compared to manual configuration.
Compared to similar skills
seo-metadata-patterns side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| seo-metadata-patterns (this skill) | 0 | 2mo | No flags | Intermediate |
| nextjs-developer | 328 | 2mo | No flags | Advanced |
| landing-page-guide | 1 | 9mo | Review | Intermediate |
| nuxt-seo | 2 | 6mo | Review | Beginner |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
nextjs-developer
zenobi-us
Expert Next.js developer mastering Next.js 14+ with App Router and full-stack features. Specializes in server components, server actions, performance optimization, and production deployment with focus on building fast, SEO-friendly applications.
landing-page-guide
bear2u
Comprehensive guide for creating effective landing pages using Next.js or React. This skill should be used when users request to create landing pages, marketing pages, or product pages that require the 11 essential elements for high-converting landing pages. Specifically designed for Next.js 14+ App Router with ShadCN UI components.
nuxt-seo
onmax
Nuxt SEO meta-module with robots, sitemap, og-image, schema-org. Use when configuring SEO, generating sitemaps, creating OG images, or adding structured data.
nextjs-seo-foundations
zou9229
Next.js 14 应用的生产级 SEO 工程化规范 (Metadata, Schema, Performance)
tanstack-seo
magnusrodseth
Complete SEO setup for TanStack Router / TanStack Start projects. Covers: dynamic OG image generation with Satori + Resvg, centralized SEO config and meta tag helpers, structured data (JSON-LD) for Organization/WebSite/Article/FAQ/Breadcrumb/Software schemas, dynamic XML sitemap, robots.txt, llms.tx
social-metadata-hardening
boisenoise
Fix social sharing previews so URLs render as rich cards on Facebook, LinkedIn, X/Twitter, WhatsApp, Telegram, and more. Covers OG tags, Twitter cards, absolute image URLs, and debugging.