Openclaudia-skills schema-markup
Generate Schema.org structured data (JSON-LD) for any page type. Use when the user says "schema markup", "structured data", "JSON-LD", "rich snippets", "rich results", "FAQ schema", "product schema", "article schema", "breadcrumb schema", "organization schema", "how-to schema", "review schema", or asks about structured data for SEO.
git clone https://github.com/OpenClaudia/openclaudia-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/OpenClaudia/openclaudia-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/schema-markup" ~/.claude/skills/openclaudia-openclaudia-skills-schema-markup && rm -rf "$T"
skills/schema-markup/SKILL.mdSchema Markup Generator Skill
You are an expert in Schema.org structured data and Google's rich results requirements. Generate valid, complete JSON-LD markup that maximizes eligibility for Google rich results.
Supported Schema Types
This skill supports the following schema types. When the user asks for schema, determine which type(s) are appropriate based on the page content.
1. Article / BlogPosting / NewsArticle
Use for: Blog posts, news articles, editorial content Rich result: Article carousel, headline in search
{ "@context": "https://schema.org", "@type": "Article", "headline": "Your Article Title (max 110 characters)", "description": "Brief description of the article (max 160 characters)", "image": [ "https://example.com/image-16x9.jpg", "https://example.com/image-4x3.jpg", "https://example.com/image-1x1.jpg" ], "datePublished": "2025-01-15T08:00:00+00:00", "dateModified": "2025-01-20T10:30:00+00:00", "author": [{ "@type": "Person", "name": "Author Name", "url": "https://example.com/author/name", "jobTitle": "Senior Editor", "sameAs": [ "https://twitter.com/authorhandle", "https://linkedin.com/in/authorname" ] }], "publisher": { "@type": "Organization", "name": "Publisher Name", "logo": { "@type": "ImageObject", "url": "https://example.com/logo.png", "width": 600, "height": 60 } }, "mainEntityOfPage": { "@type": "WebPage", "@id": "https://example.com/article-url" }, "wordCount": 2500, "articleSection": "Technology", "keywords": ["keyword1", "keyword2", "keyword3"], "isAccessibleForFree": true }
Google requirements:
is required (max 110 characters)headline
is required (provide 3 aspect ratios: 16:9, 4:3, 1:1; each > 696px wide)image
is required (ISO 8601 format)datePublished
is requiredauthor.name- For
, also addNewsArticle
if applicabledateline - For
,BlogPosting
changes to@type"BlogPosting"
2. Product
Use for: Product pages, e-commerce listings Rich result: Product snippet with price, availability, reviews
{ "@context": "https://schema.org", "@type": "Product", "name": "Product Name", "description": "Product description", "image": [ "https://example.com/product-1.jpg", "https://example.com/product-2.jpg" ], "sku": "SKU-12345", "mpn": "MPN-67890", "gtin13": "0123456789012", "brand": { "@type": "Brand", "name": "Brand Name" }, "offers": { "@type": "Offer", "url": "https://example.com/product", "priceCurrency": "USD", "price": "99.99", "priceValidUntil": "2025-12-31", "availability": "https://schema.org/InStock", "itemCondition": "https://schema.org/NewCondition", "seller": { "@type": "Organization", "name": "Seller Name" }, "shippingDetails": { "@type": "OfferShippingDetails", "shippingRate": { "@type": "MonetaryAmount", "value": "0", "currency": "USD" }, "deliveryTime": { "@type": "ShippingDeliveryTime", "handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" }, "transitTime": { "@type": "QuantitativeValue", "minValue": 1, "maxValue": 5, "unitCode": "DAY" } }, "shippingDestination": { "@type": "DefinedRegion", "addressCountry": "US" } }, "hasMerchantReturnPolicy": { "@type": "MerchantReturnPolicy", "applicableCountry": "US", "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow", "merchantReturnDays": 30, "returnMethod": "https://schema.org/ReturnByMail", "returnFees": "https://schema.org/FreeReturn" } }, "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.5", "bestRating": "5", "ratingCount": "142" }, "review": [{ "@type": "Review", "author": { "@type": "Person", "name": "Reviewer Name" }, "datePublished": "2025-01-10", "reviewBody": "Review text here", "reviewRating": { "@type": "Rating", "ratingValue": "5", "bestRating": "5" } }] }
Google requirements:
is requiredname
,offers
, orreview
- at least one requiredaggregateRating
andoffers.price
required if offers presentoffers.priceCurrency
must use Schema.org enum valuesoffers.availability- As of 2024,
andshippingDetails
are recommended for merchant listingshasMerchantReturnPolicy
3. FAQPage
Use for: FAQ sections, Q&A pages Rich result: Expandable FAQ in search results
{ "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "What is the first question?", "acceptedAnswer": { "@type": "Answer", "text": "<p>The answer with <strong>HTML formatting</strong> allowed. You can include <a href=\"https://example.com\">links</a>.</p>" } }, { "@type": "Question", "name": "What is the second question?", "acceptedAnswer": { "@type": "Answer", "text": "Plain text answers also work." } } ] }
Google requirements:
- Each
must have exactly oneQuestionacceptedAnswer - Answer
can include HTML:text
through<h2>
,<h6>
,<br>
,<ol>
,<ul>
,<li>
,<a>
,<p>
,<b>
,<strong>
,<i><em> - Must be visible on the page (not hidden behind tabs/accordions without proper implementation)
- Google may show up to 3 FAQ rich results per page
- Do not use for advertising purposes
4. HowTo
Use for: Tutorial pages, step-by-step guides, DIY instructions Rich result: Step-by-step display in search results
{ "@context": "https://schema.org", "@type": "HowTo", "name": "How to Do Something", "description": "Brief description of the task", "image": { "@type": "ImageObject", "url": "https://example.com/howto-main.jpg", "height": "406", "width": "305" }, "totalTime": "PT30M", "estimatedCost": { "@type": "MonetaryAmount", "currency": "USD", "value": "20" }, "supply": [ { "@type": "HowToSupply", "name": "Supply item 1" }, { "@type": "HowToSupply", "name": "Supply item 2" } ], "tool": [ { "@type": "HowToTool", "name": "Tool 1" } ], "step": [ { "@type": "HowToStep", "name": "Step 1 Title", "text": "Detailed instructions for step 1.", "url": "https://example.com/howto#step1", "image": "https://example.com/step1.jpg" }, { "@type": "HowToStep", "name": "Step 2 Title", "text": "Detailed instructions for step 2.", "url": "https://example.com/howto#step2", "image": "https://example.com/step2.jpg" } ] }
Google requirements:
is requiredname
array is required with at least one stepstep- Each step needs either
ortext
withitemListElement
/HowToDirectionHowToTip
uses ISO 8601 duration format (PT1H30M = 1 hour 30 minutes)totalTime- Do not use HowTo for recipes (use Recipe schema instead)
5. Organization
Use for: Homepage, about page, company information Rich result: Knowledge panel, logo in search
{ "@context": "https://schema.org", "@type": "Organization", "name": "Company Name", "alternateName": "Company Abbreviation", "url": "https://example.com", "logo": { "@type": "ImageObject", "url": "https://example.com/logo.png", "width": 512, "height": 512 }, "description": "Company description", "foundingDate": "2020-01-01", "founder": { "@type": "Person", "name": "Founder Name" }, "address": { "@type": "PostalAddress", "streetAddress": "123 Main St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94102", "addressCountry": "US" }, "contactPoint": [{ "@type": "ContactPoint", "telephone": "+1-555-555-5555", "contactType": "customer service", "areaServed": "US", "availableLanguage": "English" }], "sameAs": [ "https://twitter.com/company", "https://linkedin.com/company/company", "https://facebook.com/company", "https://github.com/company" ], "numberOfEmployees": { "@type": "QuantitativeValue", "minValue": 10, "maxValue": 50 } }
6. LocalBusiness
Use for: Local business pages, Google Business Profile support Rich result: Local business panel, map results
{ "@context": "https://schema.org", "@type": "LocalBusiness", "@id": "https://example.com/#business", "name": "Business Name", "description": "Business description", "url": "https://example.com", "telephone": "+1-555-555-5555", "email": "info@example.com", "image": "https://example.com/storefront.jpg", "logo": "https://example.com/logo.png", "priceRange": "$$", "address": { "@type": "PostalAddress", "streetAddress": "123 Main St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94102", "addressCountry": "US" }, "geo": { "@type": "GeoCoordinates", "latitude": "37.7749", "longitude": "-122.4194" }, "openingHoursSpecification": [ { "@type": "OpeningHoursSpecification", "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "opens": "09:00", "closes": "17:00" }, { "@type": "OpeningHoursSpecification", "dayOfWeek": "Saturday", "opens": "10:00", "closes": "14:00" } ], "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.7", "bestRating": "5", "ratingCount": "312" }, "areaServed": { "@type": "City", "name": "San Francisco" }, "hasMap": "https://maps.google.com/?cid=123456789" }
Google requirements:
,name
are requiredaddress- Use specific subtypes when possible:
,Restaurant
,Dentist
,LegalService
, etc.RealEstateAgent
coordinates should be accurate to the business locationgeo
must reflect actual business hoursopeningHoursSpecification
7. BreadcrumbList
Use for: Any page with breadcrumb navigation Rich result: Breadcrumb trail in search results
{ "@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 Title" } ] }
Google requirements:
must be sequential starting at 1position- Last item should not have
(it's the current page)item - Must match the visible breadcrumb on the page
8. Review / AggregateRating
Use for: Review pages, product reviews, service reviews Rich result: Star rating in search results
{ "@context": "https://schema.org", "@type": "Review", "name": "Review Title", "reviewBody": "Full review text...", "datePublished": "2025-01-15", "author": { "@type": "Person", "name": "Reviewer Name" }, "itemReviewed": { "@type": "Product", "name": "Product Being Reviewed", "image": "https://example.com/product.jpg" }, "reviewRating": { "@type": "Rating", "ratingValue": "4", "bestRating": "5", "worstRating": "1" }, "publisher": { "@type": "Organization", "name": "Review Site Name" } }
Google requirements:
is required (must be a validauthor
orPerson
)Organization
is requireditemReviewed
is recommendedreviewRating- Self-serving reviews (reviewing your own product) are against guidelines
Multi-Schema Pages
Most pages need multiple schema types. Combine them using
@graph:
{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://example.com/#organization", "name": "Company Name", "url": "https://example.com", "logo": "https://example.com/logo.png" }, { "@type": "WebSite", "@id": "https://example.com/#website", "url": "https://example.com", "name": "Site Name", "publisher": { "@id": "https://example.com/#organization" } }, { "@type": "WebPage", "@id": "https://example.com/page/#webpage", "url": "https://example.com/page/", "name": "Page Title", "isPartOf": { "@id": "https://example.com/#website" } }, { "@type": "Article", "mainEntityOfPage": { "@id": "https://example.com/page/#webpage" }, "headline": "Article Title", "author": { "@type": "Person", "name": "Author" }, "publisher": { "@id": "https://example.com/#organization" }, "datePublished": "2025-01-15" }, { "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com" }, { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://example.com/blog" }, { "@type": "ListItem", "position": 3, "name": "Article Title" } ] } ] }
Generation Process
When the user asks for schema markup:
- Determine page type - Ask what kind of page this is for (or infer from context)
- Gather information - Ask for or collect the required fields. If the user provides a URL, fetch it to extract data.
- Select schema types - Choose all applicable schemas (most pages need 2-4 types)
- Generate JSON-LD - Create complete, valid markup
- Validate - Check against Google's requirements for each type
- Provide implementation instructions - Tell the user exactly where to place it
Implementation Instructions
For Next.js App Router:
// In your page component or layout export default function Page() { const jsonLd = {/* generated schema */}; return ( <> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> {/* page content */} </> ); }
For Next.js with next/head (Pages Router):
import Head from 'next/head'; export default function Page() { const jsonLd = {/* generated schema */}; return ( <> <Head> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> </Head> {/* page content */} </> ); }
For plain HTML:
<head> <script type="application/ld+json"> {/* generated schema */} </script> </head>
Validation
After generating the markup, remind the user to validate using:
- Google Rich Results Test: https://search.google.com/test/rich-results
- Schema.org Validator: https://validator.schema.org/
Common Mistakes to Avoid
- Do not add schema for content that is not visible on the page
- Do not use
schema for self-serving reviews of your own businessReview - Do not markup content behind a paywall as
isAccessibleForFree: true - Do not use fake or placeholder data in production schema
- Do not add
without actual user reviewsAggregateRating - Always use absolute URLs, never relative
- Always use ISO 8601 date format
must be a future datepriceValidUntil
must use full Schema.org URL (e.g.,availability
)https://schema.org/InStock- Image URLs must be crawlable and indexable