Structured data & schema markup
What is structured data?
Structured data helps search engines understand the content of your pages better. You can add schema markup using JSON-LD by inserting it directly into the page. This lets the markup exist in the HTML sent from the server, which is important for SEO.
Schema markup is a structured data format that helps search engines better understand your website’s content. If you are a cooking website, recipe schema can provide additional context to search engines about your recipes.
Why use structured data?
Benefit Description Rich Snippets Enhanced search results with ratings, images, prices, and more Better Understanding Helps Google understand your content context Voice Search Structured data powers voice assistant responses Knowledge Panels Can contribute to Google’s knowledge graph Higher CTR Rich results typically have higher click-through rates
Common schema types
Best for blog posts, news articles, and editorial content. {
"@context" : "https://schema.org" ,
"@type" : "Article" ,
"headline" : "Article Title" ,
"author" : { "@type" : "Person" , "name" : "Author Name" },
"datePublished" : "2024-01-15" ,
"image" : "https://example.com/image.jpg"
}
For company or brand pages. {
"@context" : "https://schema.org" ,
"@type" : "Organization" ,
"name" : "Company Name" ,
"url" : "https://example.com" ,
"logo" : "https://example.com/logo.png"
}
For e-commerce and product pages. {
"@context" : "https://schema.org" ,
"@type" : "Product" ,
"name" : "Product Name" ,
"image" : "https://example.com/product.jpg" ,
"offers" : {
"@type" : "Offer" ,
"price" : "99.99" ,
"priceCurrency" : "USD"
}
}
For frequently asked questions pages. {
"@context" : "https://schema.org" ,
"@type" : "FAQPage" ,
"mainEntity" : [{
"@type" : "Question" ,
"name" : "What is ButterCMS?" ,
"acceptedAnswer" : {
"@type" : "Answer" ,
"text" : "ButterCMS is a headless CMS..."
}
}]
}
For site navigation breadcrumbs. {
"@context" : "https://schema.org" ,
"@type" : "BreadcrumbList" ,
"itemListElement" : [{
"@type" : "ListItem" ,
"position" : 1 ,
"name" : "Home" ,
"item" : "https://example.com/"
}]
}
Implementing schema in Angular
In Angular, you can add schema markup using JSON-LD by inserting it directly into the page with Renderer2:
import { Component , OnInit , Renderer2 , ElementRef } from '@angular/core' ;
@ Component ({
selector: 'app-article' ,
templateUrl: './article.component.html' ,
})
export class ArticleComponent implements OnInit {
constructor ( private renderer : Renderer2 , private el : ElementRef ) {}
ngOnInit () {
const schema = {
"@context" : "https://schema.org" ,
"@type" : "Article" ,
"headline" : "Angular SEO Guide" ,
"author" : {
"@type" : "Person" ,
"name" : "Your Name"
},
"datePublished" : "2025-08-06"
};
const script = this . renderer . createElement ( 'script' );
script . type = 'application/ld+json' ;
script . text = JSON . stringify ( schema );
this . renderer . appendChild ( this . el . nativeElement , script );
}
}
This code adds schema.org structured data using JSON-LD inside a <script> tag. Search engines read this to understand the page content better.
Implementing schema in React/Next.js
For React and Next.js applications, you can create a reusable component for structured data:
// components/StructuredData.jsx
import Head from 'next/head' ;
export function StructuredData ({ data }) {
return (
< Head >
< script
type = "application/ld+json"
dangerouslySetInnerHTML = { { __html: JSON . stringify ( data ) } }
/>
</ Head >
);
}
// Usage in a page component
import { StructuredData } from '../components/StructuredData' ;
export default function BlogPost ({ post }) {
const articleSchema = {
"@context" : "https://schema.org" ,
"@type" : "Article" ,
"headline" : post . seo_title || post . title ,
"author" : {
"@type" : "Person" ,
"name" : ` ${ post . author . first_name } ${ post . author . last_name } `
},
"datePublished" : post . published ,
"dateModified" : post . updated ,
"image" : post . featured_image ,
"description" : post . meta_description
};
return (
<>
< StructuredData data = { articleSchema } />
{ /* Rest of your component */ }
</>
);
}
Using ButterCMS data for schema
When fetching content from ButterCMS, map your content fields to schema properties:
Blog Post article schema
// Fetch blog post from ButterCMS
const response = await butter . post . retrieve ( 'my-post-slug' );
const post = response . data . data ;
// Create Article schema
const articleSchema = {
"@context" : "https://schema.org" ,
"@type" : "Article" ,
"headline" : post . seo_title || post . title ,
"description" : post . meta_description || post . summary ,
"image" : post . featured_image ,
"author" : {
"@type" : "Person" ,
"name" : ` ${ post . author . first_name } ${ post . author . last_name } ` ,
"url" : `/author/ ${ post . author . slug } `
},
"publisher" : {
"@type" : "Organization" ,
"name" : "Your Site Name" ,
"logo" : {
"@type" : "ImageObject" ,
"url" : "https://yoursite.com/logo.png"
}
},
"datePublished" : post . published ,
"dateModified" : post . updated ,
"mainEntityOfPage" : {
"@type" : "WebPage" ,
"@id" : `https://yoursite.com/blog/ ${ post . slug } `
}
};
Page schema with SEO Component
// Fetch page from ButterCMS
const response = await butter . page . retrieve ( 'landing_page' , 'my-page-slug' );
const page = response . data . data ;
// Create WebPage schema using SEO component data
const pageSchema = {
"@context" : "https://schema.org" ,
"@type" : "WebPage" ,
"name" : page . fields . seo ?. title || page . name ,
"description" : page . fields . seo ?. description ,
"image" : page . fields . seo ?. og_image ,
"url" : `https://yoursite.com/ ${ page . slug } ` ,
"datePublished" : page . published ,
"dateModified" : page . updated
};
Collection items (e.g., FAQ schema)
// Fetch FAQ collection from ButterCMS
const response = await butter . content . retrieve ([ 'faq' ]);
const faqs = response . data . data . faq ;
// Create FAQPage schema
const faqSchema = {
"@context" : "https://schema.org" ,
"@type" : "FAQPage" ,
"mainEntity" : faqs . map ( faq => ({
"@type" : "Question" ,
"name" : faq . question ,
"acceptedAnswer" : {
"@type" : "Answer" ,
"text" : faq . answer
}
}))
};
Creating a schema Component in ButterCMS
You can create a dedicated Structured Data component in ButterCMS to give content editors control over schema markup:
Structured Data Component:
├── schema_type (Dropdown: Article, Product, FAQ, Organization, etc.)
├── headline (Short Text)
├── description (Long Text)
├── image (Media)
├── author_name (Short Text)
├── date_published (Date)
├── date_modified (Date)
└── additional_properties (Long Text - JSON format)
Then in your frontend:
function generateSchemaFromComponent ( schemaData ) {
const baseSchema = {
"@context" : "https://schema.org" ,
"@type" : schemaData . schema_type ,
"headline" : schemaData . headline ,
"description" : schemaData . description ,
"image" : schemaData . image ,
"datePublished" : schemaData . date_published ,
"dateModified" : schemaData . date_modified
};
// Add author if provided
if ( schemaData . author_name ) {
baseSchema . author = {
"@type" : "Person" ,
"name" : schemaData . author_name
};
}
// Merge additional properties if provided
if ( schemaData . additional_properties ) {
try {
const additionalProps = JSON . parse ( schemaData . additional_properties );
return { ... baseSchema , ... additionalProps };
} catch ( e ) {
console . warn ( 'Invalid JSON in additional_properties' );
}
}
return baseSchema ;
}
Testing your structured data
Always validate your structured data before deploying to production.
Tool Purpose URL Google Rich Results Test Test for rich snippet eligibility search.google.com/test/rich-results Schema Markup Validator Validate schema.org syntax validator.schema.org Google Search Console Monitor schema performance search.google.com/search-console
Common validation errors
Watch out for these common structured data errors:
Missing required properties (e.g., image for Article)
Invalid date formats (use ISO 8601: YYYY-MM-DD)
URLs without proper protocol (https://)
Invalid JSON syntax