Schema Diff Tool
Paste your JSON-LD. See exactly what's missing.
Compare your markup against 14 hand-curated schema.org reference examples. Results come back split by Google’s required, recommended, and optional buckets so you know what will block a rich result and what’s just polish. Everything runs in your browser. Your markup never leaves your machine.
Missing required (1)
These disqualify your markup from the Product snippet rich result.
offers.price"495.00"
Missing recommended (15)
Not blockers, but including these improves rich result quality and eligibility.
aggregateRating{ "@type": "AggregateRating", "@id": "https://xoocode.com/shop/xootee-classic#rating", "ratingValue": "4.5", "reviewCount": "2", "bestRating": "5", "worstRating": "1" }aggregateRating.ratingValue"4.5"aggregateRating.reviewCount"2"gtin13"5710911234567"mpn"XOO-CLASSIC-1945"offers.hasMerchantReturnPolicy{ "@type": "MerchantReturnPolicy", "applicableCountry": "DK", "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow", "merchantReturnDays": "30", "returnMethod": "https://schema.org/ReturnByMail", "returnFees": "https://schema.org/FreeReturn" }offers.itemCondition"https://schema.org/NewCondition"offers.priceValidUntil"2026-12-31"offers.seller{ "@id": "https://xoocode.com/shop/dunmore-1290" }offers.shippingDetails{ "@type": "OfferShippingDetails", "shippingRate": { "@type": "MonetaryAmount", "value": "49.00", "currency": "DKK" }, "shippingDestination": { ...review[ { "@type": "Review", "author": { "@type": "Person", "name": "Dana Crook" }, "datePublished": "2024-11-03", ...review.author{ "@type": "Person", "name": "Dana Crook" }review.reviewRating{ "@type": "Rating", "ratingValue": "4", "bestRating": "5", "worstRating": "1" }review.reviewRating.ratingValue"4"url"https://xoocode.com/shop/xootee-classic"
Missing optional (46)
Extras XooCode's example demonstrates. Add only if they make sense for your content.
aggregateRating.bestRating"5"aggregateRating.worstRating"1"audience{ "@type": "PeopleAudience", "suggestedGender": "unisex", "suggestedMinAge": "16" }audience.suggestedGender"unisex"audience.suggestedMinAge"16"award"Scandinavian Design Finalist 2024"category"Apparel > T-Shirts > Graphic"color"Natural Bone"isRelatedTo{ "@type": "Product", "name": "XooShoes Dunmore Edition", "url": "https://xoocode.com/shop/xooshoes-dunmore" }isRelatedTo.name"XooShoes Dunmore Edition"isRelatedTo.url"https://xoocode.com/shop/xooshoes-dunmore"isSimilarTo{ "@type": "Product", "name": "XooPremium Hooded Long Sleeve", "url": "https://xoocode.com/shop/xoopremium-hoodie" }isSimilarTo.name"XooPremium Hooded Long Sleeve"isSimilarTo.url"https://xoocode.com/shop/xoopremium-hoodie"manufacturer{ "@id": "https://xoocode.com#organization" }material"100% Organic Cotton"offers.hasMerchantReturnPolicy.applicableCountry"DK"offers.hasMerchantReturnPolicy.merchantReturnDays"30"offers.hasMerchantReturnPolicy.returnFees"https://schema.org/FreeReturn"offers.hasMerchantReturnPolicy.returnMethod"https://schema.org/ReturnByMail"offers.hasMerchantReturnPolicy.returnPolicyCategory"https://schema.org/MerchantReturnFiniteReturnWindow"offers.shippingDetails.deliveryTime{ "@type": "ShippingDeliveryTime", "handlingTime": { "@type": "QuantitativeValue", "minValue": "0", "maxValue": "1", "unitCode": "DAY" }, ...offers.shippingDetails.deliveryTime.handlingTime{ "@type": "QuantitativeValue", "minValue": "0", "maxValue": "1", "unitCode": "DAY" }offers.shippingDetails.deliveryTime.handlingTime.maxValue"1"offers.shippingDetails.deliveryTime.handlingTime.minValue"0"offers.shippingDetails.deliveryTime.handlingTime.unitCode"DAY"offers.shippingDetails.deliveryTime.transitTime{ "@type": "QuantitativeValue", "minValue": "1", "maxValue": "3", "unitCode": "DAY" }offers.shippingDetails.deliveryTime.transitTime.maxValue"3"offers.shippingDetails.deliveryTime.transitTime.minValue"1"offers.shippingDetails.deliveryTime.transitTime.unitCode"DAY"offers.shippingDetails.shippingDestination{ "@type": "DefinedRegion", "addressCountry": "DK" }offers.shippingDetails.shippingDestination.addressCountry"DK"offers.shippingDetails.shippingRate{ "@type": "MonetaryAmount", "value": "49.00", "currency": "DKK" }offers.shippingDetails.shippingRate.currency"DKK"offers.shippingDetails.shippingRate.value"49.00"productID"xoo:XOO-TEE-CL-001"releaseDate"2024-09-01"review.author.name"Dana Crook"review.datePublished"2024-11-03"review.name"Comfortable and conversation-starting"review.reviewBody"Picked this up at the Dunmore store after reviewing the shop itself last year. The print holds up after many washes and the cotton is genuinely soft. Love that the graphic tells a story."review.reviewRating.bestRating"5"review.reviewRating.worstRating"1"weight{ "@type": "QuantitativeValue", "value": "220", "unitCode": "GRM" }weight.unitCode"GRM"weight.value"220"
Properties in your markup not in XooCode's example (1)
Informational only. This isn't wrong. Schema.org has over 1400 properties and our example can't demonstrate all of them. Anything sensible you've added is yours to keep.
brand.nameHow to use the Schema Diff Tool
The tool is built around a single workflow: paste your existing JSON-LD, pick the schema type you believe it represents, read the diff, fix the missing properties in your own source. It takes about ninety seconds from first paste to a fully-classified result.
- 1
Pick a reference example
Use the Reference example dropdown to choose which of XooCode’s 14 hand-curated examples you want to diff against. If you’re marking up a product page, pick Product. If you’re marking up a recipe, pick Recipe. The reference is the shape you’re aiming for; the diff tells you how far away you are. - 2
Paste your JSON-LD
Paste the JSON-LD you’re currently serving on the page. You can paste with or without the<script type="application/ld+json">wrapper — the tool strips that automatically. HTML-encoded characters (<,") from a WordPress export or an HTML source view are also decoded. - 3
Read the four result sections
The result lands in four collapsible sections: Missing required(red — these will block your rich result), Missing recommended(amber — these hurt quality but don’t disqualify you), Missing optional(grey — add only if they make sense), and Extra properties(your markup has things the reference doesn’t — not wrong, just informational). - 4
Click Why? for the rationale
Every required and recommended row has a Why? button that pops a tooltip explaining the rich result that property governs and links to the exact Google Search Central docs page so you can read the requirement in context. This is the part that turns the diff from a checklist into a learning tool. - 5
Fix your source and re-paste
The tool doesn’t edit your markup for you on purpose. You paste, read, fix in your own template or CMS, paste again. Two or three iterations and you end up with markup that matches our reference shape and passes Google’s Rich Results Test.
What the tool actually checks
The diff runs on property paths, not values. For every key in your JSON-LD object it produces a dotted path like offers.price or aggregateRating.reviewCount, builds the same set of paths for the reference example, and computes the two set differences: what the reference has that you don’t (missing) and what you have that the reference doesn’t (extra).
Arrays collapse. An offers key whose value is an array of Offer objects produces one path per unique sub-key across all elements, not one path per element. This matches how Google’s rich result eligibility works: the first valid offer in an array is enough to qualify, so the diff doesn’t punish you for having one or ten.
Every missing path is then classified against the reference example’s propertyStatus map, which is hand-authored from the current Google Search Gallery requirements for that schema type. Paths not in either the required or recommended list default to optional.
How the diff works under the hood
Nothing about the Schema Diff Tool is magical. It’s about fifty lines of TypeScript wrapped in a React textarea. The entire algorithm lives in a pure function called computeDiff that takes a string (your markup) and a reference example object, and returns a discriminated union describing the result.
Step 1 — parse and strip
We strip the <script type="application/ld+json"> wrapper if present, decode any HTML entities (<, ", &), and run the result through JSON.parse. If parsing fails we return an error state and the UI renders the parse message inline instead of a diff.
Step 2 — collect paths
We walk the parsed object depth-first, building a sorted set of dotted paths for every non-@-prefixed key. @context, @type, @id, and any other JSON-LD keyword is skipped because those are syntax, not content. The walker collapses arrays: an array of Offers produces one set of paths per unique sub-key across all elements.
Step 3 — set difference
We compute missing = example \ user and extra = user \ example as plain JavaScript Sets. If the intersection is empty we assume you pasted the wrong schema type and render the mismatch banner instead of a diff. This heuristic catches the most common paste error — pasting a Recipe schema while the Product reference is selected.
Step 4 — classify missing paths
For every path in the missing set we look up its status against the reference example’s hand-curated propertyStatus map and push it into one of three buckets: required, recommended, or optional. The map is committed to source alongside the example JSON so reviewers can audit both together.
Step 5 — render with rationale
The UI renders each bucket as a collapsible <details> section with a Why? popover on required and recommended rows. The popover links to the specific Google Search Central docs page for the rich result that property governs, so the tool doubles as a teaching interface instead of just a checklist.
Required vs recommended vs optional: the Google cheat sheet
These three words have specific meanings at Google Search and they don’t always match how schema.org uses the same words. This is the single most common source of confusion for authors who land on the schema.org vocabulary and try to reverse-engineer Google’s rich result rules from the official docs.
Required (by Google)
A property Google’s parser will reject your markup without. Missing a single required property means no rich result — not a reduced one, not a warning, just silence. For Product, the required set is name, image, offers, offers.price, offers.priceCurrency, and offers.availability. For Recipe it’s name, image, recipeIngredient, and recipeInstructions. Every rich result type has its own list.
Recommended (by Google)
A property whose absence doesn’t disqualify you but visibly degrades the result. For Product, missing aggregateRating means your result renders without the star rating — which is the visual that makes a Product snippet earn clicks. The rich result still appears, but it’s a pale version of what it could be. Treat recommended properties as “required for quality” even though they’re labeled differently.
Optional (XooCode’s label)
A property the reference example demonstrates that isn’t on either of Google’s lists. These are the properties we include because they improve the semantic richness of the markup, enable cross-references to other entities, or help non-Google consumers (language models, knowledge graph crawlers, alternative search engines). Optional properties aren’t unimportant— they’re just not on Google’s gate list.
Why we built this tool
Google’s Rich Results Test is the canonical validator. Schema.org’s Schema Markup Validator is the canonical vocabulary checker. Both are excellent tools. Neither of them answers the question most authors actually have, which is “what properties should I be including that I’m not?”.
The validators tell you whether what you have is valid. They don’t tell you whether what you have is complete. A Product schema with only name and @type is technically valid — it will parse without error. It’s also useless. The Rich Results Test will flag it as ineligible but won’t tell you what a “good” Product schema looks like at the other end.
That’s the gap the Schema Diff Tool fills. It uses our hand-authored reference examples as the comparison baseline and shows you the delta. The output is a concrete, actionable list of property names you can search for in your codebase. It’s a first step, not a replacement: once the diff is clean, run the result through the Rich Results Test to catch value-level mistakes.
Building it was also an excuse to make the reference examples earn their keep. An example that’s only consumed by readers of a single page is a document; an example that’s consumed by a diff engine becomes a shape contract. Every property we put in a reference is now a property we’re willing to recommend to every user of the tool — which raised the bar on the examples themselves.
What this tool isn't
Knowing what a tool doesn’t do is as important as knowing what it does. The Schema Diff Tool is deliberately narrow.
It IS a shape diff
It is NOT a validator
It IS a teaching tool
It is NOT a crawler
It IS private
It is NOT a generator
Authoritative sources this tool references
The tool’s rationale popovers and the reference examples themselves are built on top of primary sources. If you want to verify anything the diff tells you, these are the documents to check.
- schema.org — the canonical vocabulary. Every XooCode example references the schema.org type definition so you can read the authoritative meaning of every property.
- Google Search Gallery — the index of every rich result type Google supports, with per-type required and recommended property lists. Everything in a XooCode
propertyStatusmap is sourced from this gallery. - Google Rich Results Test — paste the same markup there after the diff is clean to catch value-level errors the shape diff can’t see.
- Schema Markup Validator — schema.org’s own validator, maintained independently from Google. Useful when you want vocabulary conformance feedback without Google’s rich result rules layered on top.
- JSON-LD 1.1 Specification — the W3C recommendation behind the notation. Useful reference when you start hitting advanced features like
@id,@graph, and context reuse.