Structured data is a standardized format for providing information about a page and classifying the page content. Search engines use structured data to understand page content better and display rich results (rich snippets) in search results.
Why Structured Data Matters for SEO
Structured data doesn't directly affect rankings, but it provides significant SEO benefits:
- Rich snippets: Enhanced search results with ratings, prices, availability
- Knowledge panels: Brand and business information displayed prominently
- Carousel results: Product and article carousels in search
- Voice search: Better answers for voice assistants
- Click-through rate: Rich results attract more clicks than plain results
Types of Rich Results
| Schema Type | Rich Result | Common Use |
|---|---|---|
| Product | Price, availability, reviews | E-commerce |
| Article | Headline, image, date | Blogs, news |
| Recipe | Ratings, time, ingredients | Food sites |
| FAQ | Expandable Q&A | Support pages |
| HowTo | Step-by-step instructions | Tutorials |
| LocalBusiness | Hours, address, reviews | Local SEO |
| Event | Date, location, tickets | Event pages |
| BreadcrumbList | Navigation path | All pages |
JSON-LD Format
JSON-LD (JavaScript Object Notation for Linked Data) is Google's preferred structured data format:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "Page Title",
"description": "Page description"
}
</script>
Why JSON-LD Over Microdata
- Easier to maintain: Separate from HTML markup
- No HTML modification: Can add via tag manager
- Easier validation: Clean JSON structure
- Google preference: Explicitly recommended by Google
Common Schema Types
Product Schema
For e-commerce product pages:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Blue Widget Pro",
"image": [
"https://example.com/photos/widget-1.jpg",
"https://example.com/photos/widget-2.jpg"
],
"description": "Premium widget with advanced features",
"sku": "WIDGET-PRO-001",
"mpn": "WGT-001-BLU",
"brand": {
"@type": "Brand",
"name": "WidgetCo"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/widgets/blue-widget-pro",
"priceCurrency": "USD",
"price": 49.99,
"priceValidUntil": "2025-12-31",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"seller": {
"@type": "Organization",
"name": "Example Store"
}
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.7,
"reviewCount": 156
},
"review": [
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": 5,
"bestRating": 5
},
"author": {
"@type": "Person",
"name": "Jane Smith"
},
"datePublished": "2024-10-15",
"reviewBody": "Excellent widget, works perfectly!"
}
]
}
</script>
Article Schema
For blog posts and news articles:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "How to Optimize Core Web Vitals for Better SEO",
"image": [
"https://example.com/images/article-hero.jpg"
],
"datePublished": "2024-10-01T08:00:00+00:00",
"dateModified": "2024-10-15T10:30:00+00:00",
"author": {
"@type": "Person",
"name": "John Doe",
"url": "https://example.com/authors/john-doe"
},
"publisher": {
"@type": "Organization",
"name": "Example Blog",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
},
"description": "A comprehensive guide to improving your website's Core Web Vitals scores.",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://example.com/blog/core-web-vitals-optimization"
}
}
</script>
FAQ Schema
For FAQ sections and support pages:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is structured data?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Structured data is a standardized format for providing information about a page and classifying the page content. It helps search engines understand your content better."
}
},
{
"@type": "Question",
"name": "Does structured data improve SEO rankings?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Structured data doesn't directly impact rankings, but it can improve click-through rates by enabling rich results in search, which indirectly benefits SEO."
}
},
{
"@type": "Question",
"name": "What format should I use for structured data?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Google recommends JSON-LD format. It's easier to implement and maintain than microdata or RDFa because it's separate from your HTML markup."
}
}
]
}
</script>
LocalBusiness Schema
For local business pages:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Example Coffee Shop",
"image": "https://example.com/photos/shop.jpg",
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Main Street",
"addressLocality": "Springfield",
"addressRegion": "IL",
"postalCode": "62701",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 39.7817,
"longitude": -89.6501
},
"url": "https://example.com",
"telephone": "+1-555-123-4567",
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "07:00",
"closes": "18:00"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Saturday"],
"opens": "08:00",
"closes": "16:00"
}
],
"priceRange": "$$",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.5,
"reviewCount": 89
}
}
</script>
BreadcrumbList Schema
For navigation breadcrumbs:
<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": "Products",
"item": "https://example.com/products/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Widgets",
"item": "https://example.com/products/widgets/"
},
{
"@type": "ListItem",
"position": 4,
"name": "Blue Widget Pro"
}
]
}
</script>
Organization Schema
For company/brand pages:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Example Company",
"url": "https://example.com",
"logo": "https://example.com/logo.png",
"sameAs": [
"https://www.facebook.com/examplecompany",
"https://twitter.com/examplecompany",
"https://www.linkedin.com/company/examplecompany"
],
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+1-555-123-4567",
"contactType": "customer service",
"availableLanguage": ["English", "Spanish"]
}
}
</script>
Implementation Strategies
Server-Side Implementation
Generate structured data from your CMS or backend:
PHP/WordPress Example:
function generate_product_schema($product) {
$schema = [
'@context' => 'https://schema.org',
'@type' => 'Product',
'name' => $product->name,
'description' => $product->description,
'sku' => $product->sku,
'image' => $product->images,
'offers' => [
'@type' => 'Offer',
'price' => $product->price,
'priceCurrency' => 'USD',
'availability' => $product->in_stock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock'
]
];
return '<script type="application/ld+json">'
. json_encode($schema, JSON_UNESCAPED_SLASHES)
. '</script>';
}
Node.js/Express Example:
function generateArticleSchema(article) {
return {
'@context': 'https://schema.org',
'@type': 'Article',
headline: article.title,
image: article.featuredImage,
datePublished: article.publishedAt,
dateModified: article.updatedAt,
author: {
'@type': 'Person',
name: article.author.name,
url: article.author.url
},
publisher: {
'@type': 'Organization',
name: 'Example Blog',
logo: {
'@type': 'ImageObject',
url: 'https://example.com/logo.png'
}
}
};
}
// In your template
app.get('/blog/:slug', (req, res) => {
const article = getArticle(req.params.slug);
const schema = generateArticleSchema(article);
res.render('article', {
article,
schemaJson: JSON.stringify(schema)
});
});
Client-Side with React
import Head from 'next/head';
function ProductPage({ product }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.images,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'USD',
availability: product.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock'
}
};
return (
<>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
</Head>
{/* Product content */}
</>
);
}
Google Tag Manager
Deploy structured data via GTM:
- Create a Custom HTML tag
- Add your JSON-LD script
- Use GTM variables for dynamic values
- Trigger on specific page types
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "{{DL - Product Name}}",
"description": "{{DL - Product Description}}",
"sku": "{{DL - Product SKU}}",
"offers": {
"@type": "Offer",
"price": {{DL - Product Price}},
"priceCurrency": "USD"
}
}
</script>
Validation and Testing
Google Rich Results Test
- Visit Rich Results Test
- Enter URL or paste code
- Review detected schemas
- Check for errors and warnings
Schema.org Validator
For complete Schema.org validation:
- Visit Schema.org Validator
- Paste your JSON-LD
- Review validation results
Browser Console Test
// Check for JSON-LD on current page
const schemas = document.querySelectorAll('script[type="application/ld+json"]');
schemas.forEach((el, i) => {
try {
const data = JSON.parse(el.textContent);
console.log(`Schema ${i + 1}:`, data['@type'], data);
} catch (e) {
console.error(`Schema ${i + 1} parsing error:`, e);
}
});
Automated Testing
// Playwright test example
const { test, expect } = require('@playwright/test');
test('product page has valid structured data', async ({ page }) => {
await page.goto('/products/blue-widget');
const schema = await page.evaluate(() => {
const el = document.querySelector('script[type="application/ld+json"]');
return el ? JSON.parse(el.textContent) : null;
});
expect(schema).not.toBeNull();
expect(schema['@type']).toBe('Product');
expect(schema.name).toBeTruthy();
expect(schema.offers.price).toBeGreaterThan(0);
});
Common Mistakes
1. Missing Required Properties
// BAD: Product without offers
{
"@type": "Product",
"name": "Widget"
// Missing offers - won't generate rich result
}
// GOOD: Include required properties
{
"@type": "Product",
"name": "Widget",
"offers": {
"@type": "Offer",
"price": 29.99,
"priceCurrency": "USD"
}
}
2. Incorrect Data Types
// BAD: Price as string
"price": "29.99"
// GOOD: Price as number
"price": 29.99
3. Schema Not Matching Visible Content
Google requires structured data to match visible page content:
// BAD: Schema shows $29.99 but page shows $39.99
// This can result in manual action
// GOOD: Schema matches displayed price exactly
4. Fake Reviews
Never generate fake reviews or ratings:
// BAD: Made up ratings
"aggregateRating": {
"ratingValue": 5,
"reviewCount": 1000 // But you only have 10 reviews
}
// GOOD: Real data only
"aggregateRating": {
"ratingValue": 4.2,
"reviewCount": 47 // Actual review count
}
5. Self-Serving FAQ Markup
FAQ schema should be for genuine user questions:
// BAD: Promotional content as FAQ
{
"name": "Why is our product the best?",
"acceptedAnswer": "Because we're #1 in the industry!"
}
// GOOD: Genuine user questions
{
"name": "What is your return policy?",
"acceptedAnswer": "We offer 30-day returns for unused items..."
}
Monitoring in Search Console
- Navigate to Search Console
- Go to "Enhancements" section
- View structured data types detected
- Monitor for errors and warnings
- Track impression and click data for rich results
Checklist
- JSON-LD format used (not microdata)
- Schema validates without errors
- All required properties included
- Data matches visible page content
- No fake reviews or ratings
- Schema updates when content changes
- Tested in Rich Results Test
- Monitoring in Search Console
- Dynamic pages generate unique schemas
Related Resources
- Product Schema Validation - E-commerce specific
- FAQ Schema Check - FAQ implementation
- Local SEO - LocalBusiness schema
- Review Snippet Validation - Review markup