Google Structured Data Skill
Implement Schema.org structured data markup for Google Search rich results. This skill provides JSON-LD templates, required/recommended properties, and best practices for all major structured data types.
When to Use This Skill
Trigger this skill when:
- Adding structured data to web pages for SEO
- Implementing JSON-LD markup for rich results
- Creating Schema.org markup for articles, products, events, FAQs, recipes, videos, or local businesses
- Generating structured data dynamically with JavaScript or Google Tag Manager
- Debugging structured data issues
- Optimizing search appearance in Google Search, News, or Discover
Quick Reference
Supported Formats
- JSON-LD (Recommended) - Embedded in
<script type="application/ld+json"> - Microdata - Inline HTML attributes
- RDFa - Semantic HTML attributes
Core Principles
- Mark up visible content only - Don't mark up hidden content
- Be accurate - Markup must represent actual page content
- Be specific - Use the most specific Schema.org type
- Include all required properties - Each type has mandatory fields
- Test before deploying - Use Rich Results Test tool
Common Structured Data Types
1. Article (NewsArticle, BlogPosting)
Use for: News articles, blog posts, sports articles
Required: None (all recommended)
Recommended Properties:
headline- Article title (keep concise)image- Representative images (16x9, 4x3, 1x1 ratios; min 50K pixels)datePublished- ISO 8601 with timezonedateModified- Last modification dateauthor- Person or Organization with name and URL
{
"@context": "https://schema.org",
"@type": "NewsArticle",
"headline": "Article Title Here",
"image": [
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg"
],
"datePublished": "2024-01-05T08:00:00+08:00",
"dateModified": "2024-02-05T09:20:00+08:00",
"author": [{
"@type": "Person",
"name": "Jane Doe",
"url": "https://example.com/profile/janedoe123"
}]
}
2. Product
Use for: E-commerce product pages, product reviews
Required Properties:
name- Product name
Recommended Properties:
image- Product imagesdescription- Product descriptionbrand- Brand nameoffers- Price, availability, currencyaggregateRating- Average ratingreview- Individual reviews
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Executive Anvil",
"image": "https://example.com/anvil.jpg",
"description": "Sleek and deadly executive anvil.",
"brand": {
"@type": "Brand",
"name": "ACME"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/anvil",
"priceCurrency": "USD",
"price": 119.99,
"availability": "https://schema.org/InStock",
"priceValidUntil": "2025-12-31"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.4,
"reviewCount": 89
}
}
3. LocalBusiness
Use for: Physical business locations, restaurants, stores
Required Properties:
name- Business nameaddress- Full postal address
Recommended Properties:
telephone- With country codeopeningHoursSpecification- Business hoursgeo- Latitude/longitude (min 5 decimal places)url- Location-specific URLpriceRange- "$" to "$$$$" or price rangeservesCuisine- For restaurantsmenu- Menu URL for food establishments
{
"@context": "https://schema.org",
"@type": "Restaurant",
"name": "Dave's Steak House",
"address": {
"@type": "PostalAddress",
"streetAddress": "148 W 51st St",
"addressLocality": "New York",
"addressRegion": "NY",
"postalCode": "10019",
"addressCountry": "US"
},
"telephone": "+12122459600",
"url": "https://www.example.com/restaurant-locations/manhattan",
"servesCuisine": "American",
"priceRange": "$$$",
"geo": {
"@type": "GeoCoordinates",
"latitude": 40.761293,
"longitude": -73.982294
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "11:30",
"closes": "22:00"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Saturday"],
"opens": "16:00",
"closes": "23:00"
}
],
"menu": "https://www.example.com/menu"
}
4. FAQPage
Use for: FAQ pages on authoritative sites (government, health)
Required Properties:
mainEntity- Array of Question objects
Question Required Properties:
name- Full question textacceptedAnswer- Answer object withtextproperty
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is the return policy?",
"acceptedAnswer": {
"@type": "Answer",
"text": "<p>You can return items within 30 days of purchase.</p>"
}
},
{
"@type": "Question",
"name": "How do I track my order?",
"acceptedAnswer": {
"@type": "Answer",
"text": "<p>Use the tracking link in your confirmation email.</p>"
}
}
]
}
Allowed HTML in answers: <h1>-<h6>, <br>, <ol>, <ul>, <li>, <a>, <p>, <div>, <b>, <strong>, <i>, <em>
5. Event
Use for: Concerts, conferences, festivals, sports events
Required Properties:
name- Event title (no venue names or promotions)startDate- ISO 8601 with timezone offsetlocation- Place with name and full address
Recommended Properties:
description- Event detailsendDate- When event concludeseventStatus- EventScheduled, EventCancelled, EventPostponed, EventRescheduledimage- High-res images (1920px width, 16x9/4x3/1x1)offers- Ticket info with price, currency, availability, URLorganizer- Hosting organizationperformer- Artists or participants
{
"@context": "https://schema.org",
"@type": "Event",
"name": "The Adventures of Kira and Morrison",
"startDate": "2025-07-21T19:00-05:00",
"endDate": "2025-07-21T23:00-05:00",
"eventStatus": "https://schema.org/EventScheduled",
"location": {
"@type": "Place",
"name": "Snickerpark Stadium",
"address": {
"@type": "PostalAddress",
"streetAddress": "100 West Snickerpark Dr",
"addressLocality": "Snickertown",
"postalCode": "19019",
"addressRegion": "PA",
"addressCountry": "US"
}
},
"image": [
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg"
],
"description": "The Adventures of Kira and Morrison is coming to Snickertown.",
"offers": {
"@type": "Offer",
"url": "https://www.example.com/event_offer/12345",
"price": 30,
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"validFrom": "2024-05-21T12:00"
},
"performer": {
"@type": "PerformingGroup",
"name": "Kira and Morrison"
},
"organizer": {
"@type": "Organization",
"name": "Kira and Morrison Music",
"url": "https://kiraandmorrisonmusic.com"
}
}
6. BreadcrumbList
Use for: Site navigation hierarchy
Required Properties:
itemListElement- Array of ListItem objects
ListItem Required Properties:
position- Sequential number starting at 1name- Breadcrumb titleitem- URL (optional for final item)
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Books",
"item": "https://example.com/books"
},
{
"@type": "ListItem",
"position": 2,
"name": "Science Fiction",
"item": "https://example.com/books/sciencefiction"
},
{
"@type": "ListItem",
"position": 3,
"name": "Award Winners"
}
]
}
7. VideoObject
Use for: Video content, livestreams, tutorials
Required Properties:
name- Unique video titlethumbnailUrl- Multiple thumbnail URLsuploadDate- ISO 8601 with timezone
Recommended Properties:
contentUrl- Direct video file URLdescription- Unique descriptionduration- ISO 8601 (e.g., PT1M54S)embedUrl- Video player URLhasPart- Clip objects for key momentspublication- BroadcastEvent for LIVE badge
{
"@context": "https://schema.org",
"@type": "VideoObject",
"name": "How to Make Banana Bread",
"description": "Learn to bake delicious banana bread in 30 minutes.",
"thumbnailUrl": [
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg"
],
"uploadDate": "2024-03-31T08:00:00+08:00",
"duration": "PT15M30S",
"contentUrl": "https://example.com/video/banana-bread.mp4",
"embedUrl": "https://example.com/embed/banana-bread",
"hasPart": [
{
"@type": "Clip",
"name": "Mixing Ingredients",
"startOffset": 30,
"endOffset": 120,
"url": "https://example.com/video?t=30"
},
{
"@type": "Clip",
"name": "Baking Process",
"startOffset": 120,
"endOffset": 600,
"url": "https://example.com/video?t=120"
}
]
}
8. Recipe
Use for: Cooking recipes, food preparation guides
Required Properties:
name- Dish nameimage- Multiple images (16x9, 4x3, 1x1; min 50K pixels)
Recommended Properties:
author- Recipe creatoraggregateRating- Average ratingprepTime,cookTime,totalTime- ISO 8601 durationsrecipeYield- Servings (required if nutrition specified)recipeCategory- Meal type (dinner, dessert)recipeCuisine- Regional originrecipeIngredient- Array of ingredientsrecipeInstructions- HowToStep arraynutrition.calories- Calorie count
{
"@context": "https://schema.org/",
"@type": "Recipe",
"name": "Non-Alcoholic Pina Colada",
"image": [
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg"
],
"author": {
"@type": "Person",
"name": "Mary Stone"
},
"datePublished": "2024-03-10",
"description": "This non-alcoholic pina colada is everyone's favorite!",
"recipeCuisine": "American",
"prepTime": "PT1M",
"cookTime": "PT2M",
"totalTime": "PT3M",
"keywords": "non-alcoholic, tropical, summer",
"recipeYield": "4 servings",
"recipeCategory": "Drink",
"nutrition": {
"@type": "NutritionInformation",
"calories": "120 calories"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 5,
"ratingCount": 18
},
"recipeIngredient": [
"400ml of pineapple juice",
"100ml cream of coconut",
"ice"
],
"recipeInstructions": [
{
"@type": "HowToStep",
"name": "Blend",
"text": "Blend 400ml of pineapple juice and 100ml cream of coconut until smooth.",
"url": "https://example.com/recipe#step1",
"image": "https://example.com/photos/step1.jpg"
},
{
"@type": "HowToStep",
"name": "Fill",
"text": "Fill a glass with ice.",
"url": "https://example.com/recipe#step2"
},
{
"@type": "HowToStep",
"name": "Pour",
"text": "Pour the pineapple juice and coconut mixture over ice.",
"url": "https://example.com/recipe#step3"
}
],
"video": {
"@type": "VideoObject",
"name": "How to Make Pina Colada",
"description": "Watch how to make this refreshing drink.",
"thumbnailUrl": "https://example.com/video-thumb.jpg",
"contentUrl": "https://example.com/video.mp4",
"uploadDate": "2024-03-10T08:00:00+00:00",
"duration": "PT3M"
}
}
Multiple Items on a Page
Nesting Approach
{
"@context": "https://schema.org/",
"@type": "Recipe",
"name": "Banana Bread",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.7,
"ratingCount": 123
},
"video": {
"@type": "VideoObject",
"name": "How To Make Banana Bread"
}
}
Array Approach
[
{
"@context": "https://schema.org/",
"@type": "Recipe",
"name": "Banana Bread"
},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [...]
}
]
Dynamic Generation with JavaScript
For SPAs, dynamic content, or CMS integrations, structured data can be generated client-side using JavaScript.
Method 1: Google Tag Manager (GTM)
Best for: Sites already using GTM, marketing teams managing structured data
Setup:
- Install GTM on your site
- Add a Custom HTML tag to your container
- Insert structured data block with GTM variables
- Publish the container
- Test with Rich Results Test (URL mode)
GTM Example with Variables:
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Recipe",
"name": "{{recipe_name}}",
"image": ["{{recipe_image}}"],
"author": {
"@type": "Person",
"name": "{{recipe_author}}"
},
"datePublished": "{{recipe_date}}",
"description": "{{recipe_description}}"
}
</script>
GTM Variable Setup:
- Create Data Layer variables or Custom JavaScript variables
- Extract values from page elements or data layer
- Reduces duplication between page content and markup
Method 2: Custom JavaScript
Best for: SPAs, API-driven content, custom implementations
Basic DOM Injection:
// Create and inject structured data
function injectStructuredData(data) {
const script = document.createElement('script');
script.setAttribute('type', 'application/ld+json');
script.textContent = JSON.stringify(data);
document.head.appendChild(script);
}
// Example: Article structured data
const articleData = {
"@context": "https://schema.org",
"@type": "Article",
"headline": document.querySelector('h1').textContent,
"image": document.querySelector('article img')?.src,
"datePublished": document.querySelector('time')?.getAttribute('datetime'),
"author": {
"@type": "Person",
"name": document.querySelector('.author-name')?.textContent
}
};
injectStructuredData(articleData);
API-Driven Generation:
// Fetch data from API and inject structured data
fetch('https://api.example.com/recipes/123')
.then(response => response.json())
.then(recipe => {
const structuredData = {
"@context": "https://schema.org/",
"@type": "Recipe",
"name": recipe.title,
"image": recipe.images,
"author": {
"@type": "Person",
"name": recipe.author.name
},
"datePublished": recipe.publishedAt,
"description": recipe.summary,
"recipeIngredient": recipe.ingredients,
"recipeInstructions": recipe.steps.map((step, i) => ({
"@type": "HowToStep",
"position": i + 1,
"text": step.instruction
}))
};
const script = document.createElement('script');
script.setAttribute('type', 'application/ld+json');
script.textContent = JSON.stringify(structuredData);
document.head.appendChild(script);
});
React/Next.js Example:
import Head from 'next/head';
function ProductPage({ product }) {
const structuredData = {
"@context": "https://schema.org",
"@type": "Product",
"name": product.name,
"image": product.images,
"description": product.description,
"brand": {
"@type": "Brand",
"name": product.brand
},
"offers": {
"@type": "Offer",
"url": `https://example.com/products/${product.slug}`,
"priceCurrency": "USD",
"price": product.price,
"availability": product.inStock
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock"
}
};
return (
<>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
</Head>
{/* Page content */}
</>
);
}
Method 3: Server-Side Rendering (Recommended)
For frameworks supporting SSR (Next.js, Nuxt, etc.), include structured data in the rendered HTML output for best reliability.
// Next.js getServerSideProps example
export async function getServerSideProps({ params }) {
const product = await fetchProduct(params.id);
return {
props: {
product,
structuredData: {
"@context": "https://schema.org",
"@type": "Product",
"name": product.name,
// ... complete structured data
}
}
};
}
JavaScript Generation: Important Considerations
Testing Requirements:
- Always use URL input in Rich Results Test (not code input)
- Code input has JavaScript limitations
- Test the actual rendered page, not source code
Product Markup Warning: Dynamically-generated Product markup can:
- Reduce shopping crawl frequency
- Affect reliability for fast-changing content (price, availability)
- Require sufficient server resources for increased Google traffic
Best Practices:
- Use GTM variables to extract data from page (avoid duplication)
- Ensure JavaScript executes before Google renders
- Test with Mobile-Friendly Test to verify rendering
- Consider SSR for critical structured data
- Monitor Search Console for rendering issues
Implementation Checklist
Before Deployment
- Use JSON-LD format (recommended)
- Include all required properties for type
- Add recommended properties for quality
- Use most specific Schema.org type
- Markup matches visible page content
- Images are crawlable and indexable
- Dates use ISO 8601 with timezone
- URLs are fully qualified
Testing
- Validate with Rich Results Test
- Check Schema.org validator
- Test with URL Inspection tool in Search Console
- Verify no manual actions in Search Console
After Deployment
- Submit sitemap for recrawling
- Monitor Rich Results report in Search Console
- Allow several days for indexing
- Track performance changes
Common Mistakes to Avoid
- Marking up invisible content - Only mark up content users can see
- Misleading markup - Content must match what's marked up
- Missing required properties - Each type has mandatory fields
- Wrong timezone format - Always include UTC offset
- Using midnight (00:00:00) - Only if event actually starts at midnight
- Low-quality images - Use high-res images meeting size requirements
- Blocking Googlebot - Don't use robots.txt or noindex on structured data pages
- Duplicate markup inconsistency - All versions of a page need identical markup
- Using deprecated formats - Don't use data-vocabulary.org
Validation Tools
- Rich Results Test: https://search.google.com/test/rich-results
- Schema.org Validator: https://validator.schema.org/
- Search Console URL Inspection: Test live URLs
- Search Console Rich Results Report: Monitor status
Resources
- Google Search Central - Structured Data
- Generate Structured Data with JavaScript
- Schema.org Full Hierarchy
- Google Structured Data Guidelines
- Rich Results Gallery
Notes
- Rich results are not guaranteed even with correct markup
- Google's algorithms determine when to show rich results
- Violations can result in manual actions (loss of rich result eligibility)
- Keep markup updated when page content changes
- Test after any significant changes
Source: Google Search Central Documentation Last Updated: January 2026 Version: 1.1
Changelog
- 1.1: Added JavaScript generation section (GTM, custom JS, React/Next.js, SSR)
- 1.0: Initial release with 8 structured data types