Adding Schema Markup
Overview
Schema is the language Google and LLMs use to understand a page. Crawlers extract entities, relationships, and facts from JSON-LD with much higher confidence than from raw HTML. In 2026 the Google rich-result surface has narrowed sharply (HowTo retired, FAQPage limited to gov/health authority sites) [1][2], but the AI-citation upside has grown: pages with well-formed structured data are cited 2.5–3.2× more often in ChatGPT, Perplexity, and AI Overviews than unstructured pages [3][4].
The job: pick the right type, populate the required + key recommended fields from content that actually exists on the page, validate, ship.
When to Use
- A new content page (article, product, itinerary) is going live
- A page already has bootstrap-level
Organization + WebSite schema and now needs page-specific markup
- An audit flagged "no structured data" on important pages
- A page is getting AI-search impressions but few citations — schema gap is a likely cause
Don't use for: thin pages with no real content (Google penalizes schema-for-content-that-isn't-there [1]), legal/disclaimer/utility pages, or pages already covered by a route-level generator.
Schema Type Decision Table
| Page type |
Schema type |
Notes |
| Blog post / news article |
Article or BlogPosting |
Use NewsArticle for time-sensitive news only |
| Long-form guide |
Article + BreadcrumbList |
Add mainEntityOfPage |
| Product page |
Product + nested Offer (+ AggregateRating if real reviews exist) |
Faking ratings = manual action [1] |
| FAQ section on a page |
FAQPage |
Rich-result display deprecated for non-gov/health [2], but still drives AI citations [4] |
| Step-by-step tutorial |
HowTo |
No rich result anymore [1]; still useful for LLMs |
| Site nav / breadcrumb trail |
BreadcrumbList |
Cheap, near-universal — almost always worth adding |
| Landing / home |
Organization + WebSite |
Already shipped by seo-bootstrap — don't duplicate |
| Trip / itinerary / road-trip page |
TouristTrip + itinerary (ItemList of TouristAttraction) |
No Google rich result, but Perplexity / AI Overviews read it [5] — relevant for vibecrafting.ai-style sites |
The Flow
- Identify the page type. Read the file (
app/blog/[slug]/page.tsx, pages/products/[id].astro, etc.) and the rendered content. Pick from the table above.
- Pick the schema type. If unsure between two (Article vs BlogPosting), default to
Article — it's a superset.
- Populate from real content. Pull
headline, datePublished, author, image, description from the actual page data — never invent fields. Required fields per type are documented at schema.org and Google Search Central [6][7].
- Wrap in one block. Use a single
<script type="application/ld+json"> containing an @graph when the page has multiple entities that reference each other (Article → Author → Organization) [8]. Use separate blocks only for fully independent entities. Reuse the helper from seo-bootstrap/templates/ so the Organization @id matches site-wide.
- Validate. Run the offline schema validator
scripts/schema-check.js (node scripts/schema-check.js --file <path>) or scripts/schema-quick.py (python scripts/schema-quick.py --file <path>). This instantly checks JSON-LD syntax, catches schema.org casing errors (like Website vs WebSite), and validates Google Rich Result required/recommended fields without browser switching or external pip dependencies. If the schema-validate MCP is installed, it can also be called. Use external tools (Schema Markup Validator and Google Rich Results Test) as secondary browser confirmation [9].
- Open one PR per page or per route group. Commit message:
feat(seo): add <Type> schema to <route>.
Common Mistakes
@type: "Website" (capital S). schema.org is case-sensitive — it's WebSite. Validators silently drop the wrong casing. Same trap as bootstrap.
- Missing
@context: "https://schema.org". No context, no parse.
- JSON-LD inside
<noscript> or rendered only client-side after hydration. Most LLM crawlers and many SERP features read the initial HTML — schema must be in SSR output.
- Schema for content that isn't on the page. Inventing FAQ Q&A in JSON-LD that doesn't appear visibly is a Google manual-action target [1].
- Faking
AggregateRating. Same — manual action and AI engines downgrade trust.
- Duplicating entities across blocks instead of using
@id references inside an @graph [8].
- Forgetting to update
dateModified on refreshes — AI engines lean on freshness signals [4].
Quick Reference
// Article (minimum)
{ "@context":"https://schema.org","@type":"Article",
"headline":"...", "datePublished":"2026-04-26", "author":{"@type":"Person","name":"..."} }
// Product
{ "@context":"https://schema.org","@type":"Product",
"name":"...", "image":"...", "offers":{"@type":"Offer","price":"29","priceCurrency":"USD","availability":"https://schema.org/InStock"} }
// FAQPage
{ "@context":"https://schema.org","@type":"FAQPage",
"mainEntity":[{"@type":"Question","name":"...","acceptedAnswer":{"@type":"Answer","text":"..."}}] }
// BreadcrumbList
{ "@context":"https://schema.org","@type":"BreadcrumbList",
"itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://..."}] }
// TouristTrip
{ "@context":"https://schema.org","@type":"TouristTrip",
"name":"...", "itinerary":{"@type":"ItemList","itemListElement":[/* TouristAttraction entries */]},
"provider":{"@id":"https://site.com/#org"} }
Lifecycle
- Bootstrap-time:
Organization + WebSite only, in root layout. Handled by seo-bootstrap.
- Growth-time: add page-type schema (
Article, Product, TouristTrip) as content ships. This skill.
- Mature / decay: schema.org deprecates types and Google retires rich-result surfaces (HowTo gone, FAQPage display narrowed) [1][2]. Re-audit annually; remove rich-result-only schema that no longer renders, but keep AI-citation-relevant types like
FAQPage and HowTo for LLM consumption [4].
What next
Pair with optimizing-on-page for the full per-page polish loop (titles, H1, internal links, schema). Run auditing-technical-seo quarterly to catch schema drift.
Sources
Citations [1]–[9] resolve in SOURCES.md.
1---2name: adding-schema-markup3description: Use when adding JSON-LD structured data to a page — Article, BlogPosting, Product, FAQPage, HowTo, BreadcrumbList, Organization, WebSite, TouristTrip. Picks the right schema type for the page, populates required + key recommended fields from the actual content, validates against schema.org, and ships in a single PR. Triggers on "add schema", "add JSON-LD", "structured data", "rich results", "FAQ schema", "Article schema", "Product schema", "BreadcrumbList", "schema markup for [page]", or any time per-page schema is being added or audited.4---56# Adding Schema Markup78## Overview910Schema is the language Google and LLMs use to understand a page. Crawlers extract entities, relationships, and facts from JSON-LD with much higher confidence than from raw HTML. In 2026 the Google rich-result surface has narrowed sharply (HowTo retired, FAQPage limited to gov/health authority sites) [1][2], but the AI-citation upside has grown: pages with well-formed structured data are cited 2.5–3.2× more often in ChatGPT, Perplexity, and AI Overviews than unstructured pages [3][4].1112The job: pick the right type, populate the required + key recommended fields **from content that actually exists on the page**, validate, ship.1314## When to Use1516- A new content page (article, product, itinerary) is going live17- A page already has bootstrap-level `Organization` + `WebSite` schema and now needs page-specific markup18- An audit flagged "no structured data" on important pages19- A page is getting AI-search impressions but few citations — schema gap is a likely cause2021**Don't use for:** thin pages with no real content (Google penalizes schema-for-content-that-isn't-there [1]), legal/disclaimer/utility pages, or pages already covered by a route-level generator.2223---2425## Schema Type Decision Table2627| Page type | Schema type | Notes |28|---|---|---|29| Blog post / news article | `Article` or `BlogPosting` | Use `NewsArticle` for time-sensitive news only |30| Long-form guide | `Article` + `BreadcrumbList` | Add `mainEntityOfPage` |31| Product page | `Product` + nested `Offer` (+ `AggregateRating` if real reviews exist) | Faking ratings = manual action [1] |32| FAQ section on a page | `FAQPage` | Rich-result display deprecated for non-gov/health [2], but still drives AI citations [4] |33| Step-by-step tutorial | `HowTo` | No rich result anymore [1]; still useful for LLMs |34| Site nav / breadcrumb trail | `BreadcrumbList` | Cheap, near-universal — almost always worth adding |35| Landing / home | `Organization` + `WebSite` | Already shipped by `seo-bootstrap` — don't duplicate |36| Trip / itinerary / road-trip page | `TouristTrip` + `itinerary` (`ItemList` of `TouristAttraction`) | No Google rich result, but Perplexity / AI Overviews read it [5] — relevant for vibecrafting.ai-style sites |3738## The Flow39401. **Identify the page type.** Read the file (`app/blog/[slug]/page.tsx`, `pages/products/[id].astro`, etc.) and the rendered content. Pick from the table above.412. **Pick the schema type.** If unsure between two (Article vs BlogPosting), default to `Article` — it's a superset.423. **Populate from real content.** Pull `headline`, `datePublished`, `author`, `image`, `description` from the actual page data — never invent fields. Required fields per type are documented at schema.org and Google Search Central [6][7].434. **Wrap in one block.** Use a single `<script type="application/ld+json">` containing an `@graph` when the page has multiple entities that reference each other (Article → Author → Organization) [8]. Use separate blocks only for fully independent entities. Reuse the helper from `seo-bootstrap/templates/` so the `Organization` `@id` matches site-wide.445. **Validate.** Run the offline schema validator `scripts/schema-check.js` (`node scripts/schema-check.js --file <path>`) or `scripts/schema-quick.py` (`python scripts/schema-quick.py --file <path>`). This instantly checks JSON-LD syntax, catches schema.org casing errors (like `Website` vs `WebSite`), and validates Google Rich Result required/recommended fields without browser switching or external pip dependencies. If the `schema-validate` MCP is installed, it can also be called. Use external tools ([Schema Markup Validator](https://validator.schema.org/) and [Google Rich Results Test](https://search.google.com/test/rich-results)) as secondary browser confirmation [9].456. **Open one PR** per page or per route group. Commit message: `feat(seo): add <Type> schema to <route>`.4647## Common Mistakes4849- **`@type: "Website"` (capital S).** schema.org is case-sensitive — it's `WebSite`. Validators silently drop the wrong casing. Same trap as bootstrap.50- **Missing `@context: "https://schema.org"`.** No context, no parse.51- **JSON-LD inside `<noscript>` or rendered only client-side after hydration.** Most LLM crawlers and many SERP features read the initial HTML — schema must be in SSR output.52- **Schema for content that isn't on the page.** Inventing FAQ Q&A in JSON-LD that doesn't appear visibly is a Google manual-action target [1].53- **Faking `AggregateRating`.** Same — manual action and AI engines downgrade trust.54- **Duplicating entities across blocks** instead of using `@id` references inside an `@graph` [8].55- **Forgetting to update `dateModified`** on refreshes — AI engines lean on freshness signals [4].5657## Quick Reference5859```jsonc60// Article (minimum)61{ "@context":"https://schema.org","@type":"Article",62 "headline":"...", "datePublished":"2026-04-26", "author":{"@type":"Person","name":"..."} }6364// Product65{ "@context":"https://schema.org","@type":"Product",66 "name":"...", "image":"...", "offers":{"@type":"Offer","price":"29","priceCurrency":"USD","availability":"https://schema.org/InStock"} }6768// FAQPage69{ "@context":"https://schema.org","@type":"FAQPage",70 "mainEntity":[{"@type":"Question","name":"...","acceptedAnswer":{"@type":"Answer","text":"..."}}] }7172// BreadcrumbList73{ "@context":"https://schema.org","@type":"BreadcrumbList",74 "itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://..."}] }7576// TouristTrip77{ "@context":"https://schema.org","@type":"TouristTrip",78 "name":"...", "itinerary":{"@type":"ItemList","itemListElement":[/* TouristAttraction entries */]},79 "provider":{"@id":"https://site.com/#org"} }80```8182## Lifecycle8384- **Bootstrap-time:** `Organization` + `WebSite` only, in root layout. Handled by `seo-bootstrap`.85- **Growth-time:** add page-type schema (`Article`, `Product`, `TouristTrip`) as content ships. This skill.86- **Mature / decay:** schema.org deprecates types and Google retires rich-result surfaces (HowTo gone, FAQPage display narrowed) [1][2]. Re-audit annually; remove rich-result-only schema that no longer renders, but keep AI-citation-relevant types like `FAQPage` and `HowTo` for LLM consumption [4].8788## What next8990Pair with `optimizing-on-page` for the full per-page polish loop (titles, H1, internal links, schema). Run `auditing-technical-seo` quarterly to catch schema drift.9192## Sources9394Citations [1]–[9] resolve in [SOURCES.md](SOURCES.md).