Structured data (JSON-LD)
schema.org via script type=ld+json — the JSON blob in the head that lights up rich results in search.
Open Graph (lesson 3) describes a page so social platforms can render a card. Structured data does the same job for search engines — describe your content with schema.org vocabulary and Google may show your page with stars next to a rating, a price next to a product, an FAQ collapsible, a recipe card with cook time and calories.
The vocabulary is shared with the microdata format from semantic-html lesson 8. JSON-LD is the delivery mechanism — instead of HTML attributes sprinkled across the page, you ship a single JSON blob in a <script> tag.
JSON-LD syntax
JSON-LD lives in a <script type="application/ld+json"> element, usually in the head.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Sourdough loaf",
"description": "A 24-hour wild-yeast loaf, baked Saturdays.",
"image": "https://example.com/loaf.jpg",
"offers": {
"@type": "Offer",
"price": "8.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
</script>The structure:
@context— always"https://schema.org". Identifies the vocabulary.@type— the schema.org type (Product, Article, Recipe, Event, FAQPage, etc.).- Properties of the type as JSON keys (
name,description,image,offers). - Nested objects for sub-types (the
offershere is an Offer object inside the Product).
The <script> content is JSON, not JavaScript — keys are quoted, no comments, no trailing commas.
You can include multiple JSON-LD blocks on one page, one per type. A blog post page might have an Article block at the top and a BreadcrumbList block describing the navigation path.
A few syntax details that bite people:
- The script tag's
typeisapplication/ld+json. Get it wrong and the JSON is ignored. - The script's content does not run as JavaScript — the browser just parses the JSON.
- Use absolute URLs in image and identifier fields. Relative paths break when crawled.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "How sourdough fermentation works",
"image": "https://example.com/og/sourdough.jpg",
"datePublished": "2026-04-29T10:00:00-05:00",
"dateModified": "2026-04-30T08:30:00-05:00",
"author": {
"@type": "Person",
"name": "Maya Patel"
},
"publisher": {
"@type": "Organization",
"name": "Daily Bread Bakery",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
}
}
</script>That blob describes a blog post. Date in ISO 8601 format. Author and publisher each as their own typed sub-objects. Image and logo as absolute URLs.
Common schema.org types
A short tour of the types that earn rich results in Google.
Article/BlogPosting/NewsArticle— for editorial content. Powers the Top Stories carousel and article cards.ProductwithOfferandAggregateRating— for e-commerce. Powers product snippets with price, rating, availability.Recipe— for food. Powers recipe cards with cook time, calories, ratings, and the prominent recipe-image format.Event— for live events. Powers event listings with date, venue, ticket link.FAQPagewith nestedQuestionandAnswer— for FAQ pages. Powers expandable FAQ snippets in search.HowTo— for step-by-step instructions. Powers step-by-step rich results.BreadcrumbList— replaces the URL in the search result with the breadcrumb path.Organization/Person— for the site owner or page author. Powers knowledge-panel-style enrichments.LocalBusiness— for physical stores. Powers map results, opening hours, click-to-call.
Schema.org is huge — hundreds of types. The list above is what most projects ever need. For anything else, the schema.org documentation is the authoritative source.
When you add structured data, also add the matching content visibly to the page. Google penalizes structured data that describes things the user cannot see — a recipe page with structured data for steps but no visible steps is treated as misleading.
FAQPage JSON-LD to a page that has no visible FAQ. The page just sells a product. What's the likely consequence?What you get for it
The reason to ship structured data is the rich-result formats Google can apply. Each type unlocks a specific format. A short tour:
Product → price and rating snippet next to the search result, with stock status. Sometimes a "buy now" button on mobile.
Recipe → recipe card at the top of search with image, cook time, calories, star rating. On mobile, sometimes a swipeable carousel of recipes.
Article → eligibility for the Top Stories carousel. The article shows with a larger image and the publication name.
FAQPage → expandable FAQ entries below your search result. Each question can be tapped to reveal the answer right in the search results.
Event → event card with date, time, venue, ticket-purchase link.
BreadcrumbList → the URL in the search result is replaced by the breadcrumb path (e.g., "Home > Bread > Sourdough" instead of example.com/cat/123/...).
None of this is guaranteed. Google decides per-query whether to show a rich result. Pages with valid structured data are eligible. Pages without will never qualify.
The pattern: pick the schema.org types that match your content, ship the JSON-LD, and check Search Console for which rich results are firing. Iterate based on what gets impressions.
Testing before shipping
Two tools you should use every time you change structured data.
Google Rich Results Test — paste a URL or HTML snippet. Tells you which schema.org types Google parsed, whether each one is eligible for a rich result, and lists every error or warning.
Schema.org validator — generic schema.org validator. Catches malformed JSON, wrong property names, missing required fields. More liberal than Google's tool.
The workflow:
- Write the JSON-LD.
- Paste into the Rich Results Test.
- Fix any errors. Eligibility means Google will consider showing a rich result; warnings are not fatal but worth fixing.
- Deploy.
- After a few days, check Search Console's "Enhancements" tab. Errors there mean the data parsed but failed real-world validation; warnings mean borderline.
Don't paste structured data from one type onto a page about another type. A Recipe blob on a hardware product page parses but is garbage data — Google will either ignore it or penalize the page. Match the type to the page.
A useful picture: structured data is like filing a document with the city. The structured form gets you faster service (rich results); a wrong form gets your application rejected; the wrong information gets you in trouble.
Product JSON-LD with a price that doesn't match the price visible on the page. What's the realistic outcome?