SEOhightechnical
Add structured data markup
rule · structured-data
Structured data helps search engines understand your content and enables rich results.
Code Example
HTML
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Executive Widget",
"description": "Premium quality widget for professionals",
"image": "https://example.com/images/widget.jpg",
"offers": {
"@type": "Offer",
"price": "49.99",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
</script>Why It Matters
Structured data enables rich snippets with stars, prices, images, and FAQ dropdowns in search results—significantly increasing click-through rates.
Common Schema Types
| Type | Use For | Rich Result |
|---|---|---|
| Article | Blog posts, news | Author, date, image |
| Product | E-commerce items | Price, rating, availability |
| FAQ | FAQ pages | Expandable questions |
| HowTo | Tutorials, guides | Step-by-step instructions |
| LocalBusiness | Physical locations | Map, hours, reviews |
| Organization | Company info | Logo, social profiles |
| BreadcrumbList | Navigation path | Breadcrumb trail |
Article Schema
TSX
// components/ArticleSchema.tsx
interface ArticleSchemaProps {
title: string
description: string
image: string
datePublished: string
dateModified: string
author: string
url: string
}
export function ArticleSchema({
title,
description,
image,
datePublished,
dateModified,
author,
url,
}: ArticleSchemaProps) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: title,
description,
image,
datePublished,
dateModified,
author: {
'@type': 'Person',
name: author,
},
publisher: {
'@type': 'Organization',
name: 'Example Company',
logo: {
'@type': 'ImageObject',
url: 'https://example.com/logo.png',
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': url,
},
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}Product Schema
TSX
interface ProductSchemaProps {
name: string
description: string
image: string
price: number
currency: string
availability: 'InStock' | 'OutOfStock' | 'PreOrder'
rating?: number
reviewCount?: number
brand?: string
sku?: string
}
export function ProductSchema({
name,
description,
image,
price,
currency,
availability,
rating,
reviewCount,
brand,
sku,
}: ProductSchemaProps) {
const schema: any = {
'@context': 'https://schema.org',
'@type': 'Product',
name,
description,
image,
sku,
brand: brand ? { '@type': 'Brand', name: brand } : undefined,
offers: {
'@type': 'Offer',
price: price.toFixed(2),
priceCurrency: currency,
availability: `https://schema.org/${availability}`,
},
}
if (rating && reviewCount) {
schema.aggregateRating = {
'@type': 'AggregateRating',
ratingValue: rating,
reviewCount,
}
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}FAQ Schema
TSX
interface FAQItem {
question: string
answer: string
}
export function FAQSchema({ items }: { items: FAQItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map(item => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: {
'@type': 'Answer',
text: item.answer,
},
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}Breadcrumb Schema
TSX
interface BreadcrumbItem {
name: string
url: string
}
export function BreadcrumbSchema({ items }: { items: BreadcrumbItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url,
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}Organization Schema
TSX
export function OrganizationSchema() {
const schema = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Example Company',
url: 'https://example.com',
logo: 'https://example.com/logo.png',
contactPoint: {
'@type': 'ContactPoint',
telephone: '+1-555-555-5555',
contactType: 'customer service',
},
sameAs: [
'https://twitter.com/example',
'https://www.linkedin.com/company/example',
'https://github.com/example',
],
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}Next.js App Router Integration
TSX
// app/blog/[slug]/page.tsx
import { ArticleSchema } from '@/components/ArticleSchema'
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
return (
<>
<ArticleSchema
title={post.title}
description={post.excerpt}
image={post.image}
datePublished={post.createdAt}
dateModified={post.updatedAt}
author={post.author.name}
url={`https://example.com/blog/${params.slug}`}
/>
<article>{/* Content */}</article>
</>
)
}Exceptions
- Necessary utility or compliance pages can be intentionally brief and should not be judged by the same editorial-depth expectations as ranking-focused content.
- AI-assisted drafting is not a failure by itself; flag unsupported claims, missing editorial review, or low-originality output instead.
- When a page has both trust-signal issues and crawl/index problems, make the page eligible to rank first and then improve the content quality signals.
Standards
- Use these references as the standard for the final search-facing HTML, metadata, and crawl behavior.
- Check the implementation against Google Search Central: Search Essentials before treating the rule as satisfied.
- Check the implementation against Google Search Central documentation before treating the rule as satisfied.
Verification
Automated Checks
- Use Google Rich Results Test (opens in a new tab)
- Validate with Schema Markup Validator
- Check Google Search Console for errors
- Test in multiple browsers
Manual Checks
- Verify JSON syntax is valid