generating-seo-metadata
SEO Metadata Generator
When to use this skill
- User asks to add meta tags to a page
- User mentions Open Graph or social sharing
- User wants JSON-LD or structured data
- User asks about hreflang or internationalization
- User wants to improve SEO or search visibility
Workflow
- Identify page type and intent
- Generate base meta tags
- Create Open Graph tags
- Add Twitter Card meta
- Generate JSON-LD structured data
- Add hreflang if multilingual
- Validate output
Instructions
Step 1: Identify Page Type
Determine content type to select appropriate schema:
| Page Type | JSON-LD Schema | Priority Meta |
|---|---|---|
| Homepage | Organization, WebSite | Brand, description |
| Article/Blog | Article, BlogPosting | Author, date, image |
| Product | Product, Offer | Price, availability |
| Service | Service, LocalBusiness | Provider, area |
| FAQ | FAQPage | Questions |
| Event | Event | Date, location, price |
| Person/About | Person, ProfilePage | Name, role |
| Contact | ContactPage, LocalBusiness | Address, phone |
Step 2: Generate Base Meta Tags
Essential meta tags:
<head>
<!-- Primary Meta -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{Page Title} | {Site Name}</title>
<meta name="description" content="{150-160 char description}" />
<!-- Indexing Control -->
<meta name="robots" content="index, follow" />
<link rel="canonical" href="https://example.com/page-url" />
<!-- Optional -->
<meta name="author" content="{Author Name}" />
<meta name="keywords" content="{keyword1, keyword2, keyword3}" />
</head>
Title guidelines:
- 50-60 characters max
- Primary keyword near the start
- Include brand name with separator
- Unique per page
Description guidelines:
- 150-160 characters
- Include primary keyword naturally
- Call to action when appropriate
- Unique per page
Step 3: Open Graph Tags
Core Open Graph:
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://example.com/page" />
<meta property="og:title" content="{Title for social sharing}" />
<meta property="og:description" content="{Description for social sharing}" />
<meta property="og:image" content="https://example.com/og-image.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="{Image description}" />
<meta property="og:site_name" content="{Site Name}" />
<meta property="og:locale" content="en_US" />
Article-specific:
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2026-01-18T10:00:00Z" />
<meta property="article:modified_time" content="2026-01-18T12:00:00Z" />
<meta property="article:author" content="https://example.com/author" />
<meta property="article:section" content="Technology" />
<meta property="article:tag" content="JavaScript" />
Product-specific:
<meta property="og:type" content="product" />
<meta property="product:price:amount" content="29.99" />
<meta property="product:price:currency" content="USD" />
<meta property="product:availability" content="in stock" />
Step 4: Twitter Card Meta
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@sitehandle" />
<meta name="twitter:creator" content="@authorhandle" />
<meta name="twitter:title" content="{Title}" />
<meta name="twitter:description" content="{Description}" />
<meta name="twitter:image" content="https://example.com/twitter-image.jpg" />
<meta name="twitter:image:alt" content="{Image description}" />
Card types:
summary: Small square imagesummary_large_image: Large rectangular imageplayer: Video/audio embedapp: App install card
Step 5: JSON-LD Structured Data
Article schema:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Article Title Here",
"description": "Article description",
"image": "https://example.com/image.jpg",
"author": {
"@type": "Person",
"name": "Author Name",
"url": "https://example.com/author"
},
"publisher": {
"@type": "Organization",
"name": "Site Name",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
},
"datePublished": "2026-01-18",
"dateModified": "2026-01-18"
}
</script>
Product schema:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Product Name",
"description": "Product description",
"image": "https://example.com/product.jpg",
"brand": {
"@type": "Brand",
"name": "Brand Name"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/product",
"priceCurrency": "USD",
"price": "29.99",
"availability": "https://schema.org/InStock",
"seller": {
"@type": "Organization",
"name": "Store Name"
}
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"reviewCount": "42"
}
}
</script>
FAQ schema:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is the question?",
"acceptedAnswer": {
"@type": "Answer",
"text": "This is the answer."
}
}
]
}
</script>
LocalBusiness schema:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Business Name",
"image": "https://example.com/photo.jpg",
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Main St",
"addressLocality": "City",
"addressRegion": "State",
"postalCode": "12345",
"addressCountry": "US"
},
"telephone": "+1-555-555-5555",
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "09:00",
"closes": "17:00"
}
}
</script>
BreadcrumbList schema:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://example.com"
},
{
"@type": "ListItem",
"position": 2,
"name": "Category",
"item": "https://example.com/category"
},
{
"@type": "ListItem",
"position": 3,
"name": "Current Page"
}
]
}
</script>
Step 6: Hreflang for Multilingual Sites
<!-- Language alternates -->
<link rel="alternate" hreflang="en" href="https://example.com/page" />
<link rel="alternate" hreflang="nl" href="https://example.nl/pagina" />
<link rel="alternate" hreflang="de" href="https://example.de/seite" />
<link rel="alternate" hreflang="x-default" href="https://example.com/page" />
Common hreflang codes:
| Language | Code | With Region |
|---|---|---|
| English | en |
en-US, en-GB |
| Dutch | nl |
nl-NL, nl-BE |
| German | de |
de-DE, de-AT |
| French | fr |
fr-FR, fr-CA |
| Spanish | es |
es-ES, es-MX |
Framework Integration
Next.js (App Router):
// app/page.tsx
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Page Title | Site Name",
description: "Page description here",
openGraph: {
title: "OG Title",
description: "OG Description",
url: "https://example.com/page",
siteName: "Site Name",
images: [
{
url: "https://example.com/og.jpg",
width: 1200,
height: 630,
alt: "Image alt",
},
],
locale: "en_US",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Twitter Title",
description: "Twitter description",
images: ["https://example.com/twitter.jpg"],
},
alternates: {
canonical: "https://example.com/page",
languages: {
en: "https://example.com/page",
nl: "https://example.nl/pagina",
},
},
};
Next.js JSON-LD:
// app/page.tsx
export default function Page() {
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: "Article Title",
// ... rest of schema
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* Page content */}
</>
);
}
Nuxt 3:
<script setup lang="ts">
useSeoMeta({
title: "Page Title",
description: "Page description",
ogTitle: "OG Title",
ogDescription: "OG Description",
ogImage: "https://example.com/og.jpg",
ogUrl: "https://example.com/page",
twitterCard: "summary_large_image",
});
useHead({
link: [{ rel: "canonical", href: "https://example.com/page" }],
script: [
{
type: "application/ld+json",
children: JSON.stringify({
"@context": "https://schema.org",
"@type": "Article",
// ... schema
}),
},
],
});
</script>
Validation
Before completing:
- Title under 60 characters
- Description 150-160 characters
- Canonical URL set
- OG image is 1200x630px
- JSON-LD validates at schema.org validator
- All URLs are absolute
- Hreflang includes x-default
Validation tools:
# Test JSON-LD locally
curl -s "https://validator.schema.org/" # Use web interface
# Check meta tags
curl -s https://example.com | grep -E '<meta|<title|<link rel="canonical"'
Error Handling
- Missing required fields: JSON-LD requires certain fields per type; validate against schema.org.
- Image dimensions wrong: OG images must be at least 200x200, recommended 1200x630.
- Duplicate canonical: Only one canonical URL per page.
- Hreflang loops: Each language version must link to all others including itself.
- Invalid dates: Use ISO 8601 format (YYYY-MM-DD or full datetime).
Resources
More from wesleysmits/agent-skills
writing-product-descriptions
Creates compelling product copy for e-commerce listings. Use when the user asks about product descriptions, e-commerce copy, product pages, marketplace listings, or converting features to benefits.
20writing-long-form-content
Generates comprehensive blog post drafts with proper structure. Use when the user asks to write a full article, create blog content, draft long-form posts, or needs complete written content with SEO optimization.
16writing-youtube-video-scripts
Creates structured video scripts with hooks, segments, and CTAs. Use when the user asks about YouTube scripts, video content, video outlines, talking points, or video intros.
15generating-ebooks-and-lead-magnets
Creates comprehensive ebooks, guides, and downloadable lead magnets with chapter structure and promotional assets. Use when the user asks about ebooks, lead magnets, downloadable guides, gated content, or PDF resources.
11writing-press-releases
Generates professional press releases with headline, dateline, inverted pyramid structure, and boilerplate. Use when the user asks about press releases, media announcements, news releases, PR distribution, or journalist outreach.
11profiling-performance
Runs performance audits and suggests optimizations using Lighthouse and Web Vitals. Use when the user asks about performance, page speed, Core Web Vitals, Lighthouse scores, or wants to optimize rendering and execution.
9