Astro Structured Data Guide 2026: JSON-LD for Rich Search Results
Structured data helps search engines understand your content. A practical guide to implementing JSON-LD schema in Astro for improved SEO and rich results.
TL;DR
- JSON-LD (JavaScript Object Notation for Linked Data) helps search engines understand your content’s meaning.
- Google recommends JSON-LD as the preferred structured data format.
- Rich results (stars, FAQs, breadcrumbs) can significantly increase click-through rates.
- In Astro, add structured data via a
<script type="application/ld+json">tag in your head component. - Common schema types: WebSite, BlogPosting, Article, FAQ, Product, Organization.
- Use the
astro-seo-schemapackage for TypeScript type checking, or implement manually. - Always validate with Google’s Rich Results Test before deploying.
Why Structured Data Matters
Structured data helps search engines understand:
- What your page is about
- Who created it
- When it was published/updated
- How it relates to other content
This understanding enables:
- Rich search results (stars, images, FAQs)
- Better content indexing
- Enhanced voice search answers
- Knowledge panel inclusion
Rich Results Impact
| Rich Result Type | Potential CTR Increase |
|---|---|
| FAQ | 15-30% |
| How-to | 10-20% |
| Review stars | 20-35% |
| Breadcrumbs | 10-15% |
| Article info | 5-15% |
JSON-LD Basics
Format
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "How to Implement Structured Data",
"author": {
"@type": "Person",
"name": "John Doe"
},
"datePublished": "2026-01-27",
"description": "A complete guide to structured data."
}
Embedding in HTML
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "My Website",
"url": "https://example.com"
}
</script>
Astro Implementation
Basic Component
Create a reusable component for structured data:
---
// src/components/StructuredData.astro
interface Props {
data: Record<string, unknown>;
}
const { data } = Astro.props;
const jsonLd = {
"@context": "https://schema.org",
...data,
};
---
<script
type="application/ld+json"
set:html={JSON.stringify(jsonLd)}
/>
Usage in Layouts
---
// src/layouts/BlogPost.astro
import StructuredData from '../components/StructuredData.astro';
import BaseLayout from './BaseLayout.astro';
interface Props {
title: string;
description: string;
publishedAt: string;
updatedAt?: string;
author: string;
image?: string;
}
const { title, description, publishedAt, updatedAt, author, image } = Astro.props;
const canonicalUrl = Astro.url.href;
---
<BaseLayout>
<StructuredData
slot="head"
data={{
"@type": "BlogPosting",
headline: title,
description: description,
datePublished: publishedAt,
dateModified: updatedAt || publishedAt,
author: {
"@type": "Person",
name: author,
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": canonicalUrl,
},
image: image ? [image] : undefined,
publisher: {
"@type": "Organization",
name: "Your Company",
logo: {
"@type": "ImageObject",
url: "https://example.com/logo.png",
},
},
}}
/>
<article>
<slot />
</article>
</BaseLayout>
Website Schema
Add to your base layout for site-wide information:
---
// src/components/WebsiteSchema.astro
const siteUrl = import.meta.env.SITE;
---
<script type="application/ld+json" set:html={JSON.stringify({
"@context": "https://schema.org",
"@type": "WebSite",
name: "Your Site Name",
description: "Your site description",
url: siteUrl,
publisher: {
"@type": "Organization",
name: "Your Company",
logo: {
"@type": "ImageObject",
url: `${siteUrl}/logo.png`,
},
},
potentialAction: {
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: `${siteUrl}/search?q={search_term_string}`,
},
"query-input": "required name=search_term_string",
},
})} />
Schema Types
BlogPosting
For blog articles:
const blogPostingSchema = {
"@type": "BlogPosting",
headline: "Article Title",
description: "Article description",
image: ["https://example.com/image.jpg"],
datePublished: "2026-01-27T08:00:00+00:00",
dateModified: "2026-01-27T10:00:00+00:00",
author: {
"@type": "Person",
name: "Author Name",
url: "https://example.com/author",
},
publisher: {
"@type": "Organization",
name: "Publisher Name",
logo: {
"@type": "ImageObject",
url: "https://example.com/logo.png",
},
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": "https://example.com/blog/article-slug",
},
};
FAQ
For FAQ pages or sections:
const faqSchema = {
"@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.",
},
},
{
"@type": "Question",
name: "Why use JSON-LD?",
acceptedAnswer: {
"@type": "Answer",
text: "JSON-LD is Google's recommended format because it's easy to implement and doesn't require changes to your HTML structure.",
},
},
],
};
Organization
For your company:
const organizationSchema = {
"@type": "Organization",
name: "Company Name",
url: "https://example.com",
logo: "https://example.com/logo.png",
description: "What your company does",
sameAs: [
"https://twitter.com/yourcompany",
"https://linkedin.com/company/yourcompany",
"https://github.com/yourcompany",
],
contactPoint: {
"@type": "ContactPoint",
email: "contact@example.com",
contactType: "customer service",
},
};
BreadcrumbList
For navigation breadcrumbs:
const breadcrumbSchema = {
"@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",
item: "https://example.com/blog/article-slug",
},
],
};
Using astro-seo-schema
For TypeScript type checking:
npm install astro-seo-schema
---
// src/pages/blog/[slug].astro
import { Schema } from 'astro-seo-schema';
const post = await getEntry('blog', Astro.params.slug);
---
<Schema
item={{
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.data.title,
description: post.data.description,
datePublished: post.data.publishedAt,
dateModified: post.data.updatedAt,
author: {
"@type": "Person",
name: post.data.author,
},
}}
/>
Benefits:
- TypeScript types for Schema.org vocabulary
- Compile-time validation
- Autocompletion in IDE
Dynamic Schema Generation
From Content Collections
---
// src/pages/blog/[slug].astro
import { getEntry } from 'astro:content';
import StructuredData from '../../components/StructuredData.astro';
const { slug } = Astro.params;
const post = await getEntry('blog', slug);
const { Content } = await post.render();
const canonicalUrl = new URL(`/blog/${slug}`, Astro.site);
const schema = {
"@type": "BlogPosting",
headline: post.data.title,
description: post.data.description,
image: post.data.heroImage?.url,
datePublished: post.data.publishedAt,
dateModified: post.data.updatedAt || post.data.publishedAt,
author: {
"@type": "Organization",
name: "Your Company",
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": canonicalUrl.href,
},
};
---
<html>
<head>
<StructuredData data={schema} />
</head>
<body>
<article>
<Content />
</article>
</body>
</html>
Multiple Schema Types
You can include multiple schema types on one page:
---
import StructuredData from '../components/StructuredData.astro';
const schemas = [
{
"@type": "WebPage",
name: "Page Title",
// ...
},
{
"@type": "BreadcrumbList",
itemListElement: [/* ... */],
},
{
"@type": "FAQPage",
mainEntity: [/* ... */],
},
];
---
{schemas.map(schema => (
<StructuredData data={schema} />
))}
Validation and Testing
Google Rich Results Test
- Go to Google Rich Results Test
- Enter your URL or paste HTML
- Check for errors and warnings
- Preview how rich results will appear
Schema Markup Validator
- Go to Schema.org Validator
- Paste your JSON-LD
- Verify against Schema.org specifications
Common Errors
| Error | Fix |
|---|---|
| Missing required property | Add the required field (e.g., author for BlogPosting) |
| Invalid date format | Use ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ |
| URL doesn’t match page | Ensure mainEntityOfPage matches canonical URL |
| Image not accessible | Use absolute URLs for images |
Implementation Checklist
Setup
- Create
StructuredData.astrocomponent - Add to base layout or page templates
- Configure site URL in
astro.config.mjs
Core Schemas
- WebSite schema on all pages
- Organization schema on homepage
- BlogPosting for blog articles
- BreadcrumbList for navigation
Optional Schemas
- FAQ for FAQ pages/sections
- Product for product pages
- HowTo for tutorial content
- Event for event pages
Testing
- Validate with Rich Results Test
- Check in Google Search Console
- Monitor for crawl errors
- Review rich results appearance
FAQ
Do I need structured data for SEO?
Structured data isn’t a direct ranking factor, but it enables rich results that can significantly increase click-through rates. It also helps search engines understand your content better.
Can I have multiple schema types on one page?
Yes. You can have WebPage + BreadcrumbList + FAQ + Article all on the same page. Just include separate JSON-LD blocks for each.
Should I use JSON-LD or Microdata?
Google recommends JSON-LD. It’s easier to implement (doesn’t require HTML changes) and easier to maintain.
How do I know if my structured data is working?
Check Google Search Console > Enhancements. It shows which schema types are detected and any errors. Rich results may take weeks to appear.
Does structured data work for all sites?
Structured data is detected by all major search engines. Rich results availability varies by search engine and schema type.
How often should I update structured data?
Update whenever the underlying content changes (dates, authors, descriptions). The schema should always reflect the actual page content.
Sources & Further Reading
- Astro JSON-LD Implementation — Complete guide
- JSON-LD Schema in Astro — Practical implementation
- Adding JSON-LD to Blog — Step-by-step walkthrough
- astro-seo-schema Package — TypeScript integration
- Structured Data Best Practices — Patterns and tips
- Astro SEO Guide — Related: comprehensive SEO
- Internal Linking Strategy — Related: SEO structure
Interested in our research?
We share our work openly. If you'd like to collaborate or discuss ideas — we'd love to hear from you.
Get in Touch