Back to list
WesleySmits

generating-seo-metadata

by WesleySmits

43 production-ready skills for AI coding agents. Works with Claude, GitHub Copilot, Cursor, Windsurf, and Zed.

0🍴 0📅 Jan 18, 2026

SKILL.md


name: generating-seo-metadata description: Generates SEO meta tags, Open Graph cards, and JSON-LD structured data. Use when the user asks about meta tags, og:image, schema.org markup, hreflang, or wants to improve search visibility.

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 TypeJSON-LD SchemaPriority Meta
HomepageOrganization, WebSiteBrand, description
Article/BlogArticle, BlogPostingAuthor, date, image
ProductProduct, OfferPrice, availability
ServiceService, LocalBusinessProvider, area
FAQFAQPageQuestions
EventEventDate, location, price
Person/AboutPerson, ProfilePageName, role
ContactContactPage, LocalBusinessAddress, 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 image
  • summary_large_image: Large rectangular image
  • player: Video/audio embed
  • app: 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:

LanguageCodeWith Region
Englishenen-US, en-GB
Dutchnlnl-NL, nl-BE
Germandede-DE, de-AT
Frenchfrfr-FR, fr-CA
Spanisheses-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

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

0/5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon