If you’ve read anything about Schema.org, you’ve read the standard pitch: add structured data, get rich results in Google, maybe a nice star rating or a recipe card. That’s true, but it’s a narrow way to think about what structured data is for. The deeper value isn’t the visual reward in a search results page — it’s that you’re handing machines, AI search agents, and LLM crawlers an explicit, unambiguous description of your content instead of leaving them to guess.
Think about what a crawler sees when it hits a typical webpage: a tree of <div>s and <span>s, styled and positioned for humans, with no inherent meaning attached to any of it. A number next to some text might be a price. It might be a quantity, a rating, a year, an ID. The crawler has to infer structure from layout and hope its heuristics hold up. Schema.org lets you skip the guessing entirely. You’re not describing how something looks — you’re stating what it is.
The menu problem.
A food menu is a great example because it’s genuinely hard to parse without help. If it’s a PDF, you’re relying on OCR and layout heuristics, which is fragile at best. If it’s HTML, you’ve usually got a repeating pattern of divs: name, description, price, maybe a category heading somewhere above them. Nothing in the markup says “this price belongs to this item”, or “this item belongs to this section of the menu”. A machine has to reconstruct that relationship from proximity and formatting conventions that could easily change.
Mark it up with Schema.org’s Menu, MenuSection, MenuItem, and Offer types, and that ambiguity disappears. You’re explicitly saying: this is a menu section called “Starters”, it contains these menu items, each item has a name, a description, and an offer with a price and currency. Nothing is left for the crawler to infer. That’s the whole point — not decoration, but disambiguation.
Rich snippets are the visible tip, not the whole iceberg.
Google is explicit that only certain schema types are eligible for rich results, and that list is a small subset of the full Schema.org vocabulary. It’s easy to read that and conclude the rest isn’t worth doing. I think that’s the wrong conclusion. Search engines and AI crawlers alike are still ingesting and processing structured data even when there’s no visual payoff in the SERP — it feeds entity understanding, disambiguation, and increasingly the kind of retrieval that underpins AI-generated answers, even when there’s no snippet to show for it.
This matters even more as more traffic comes from tools that summarize or answer rather than list links. Those tools benefit from unambiguous entity data just as much as traditional crawlers do — arguably more, since they’re synthesizing an answer rather than just ranking a page. sameAs properties linking your entities to Wikidata or Wikipedia aren’t just a Knowledge Graph nicety; they’re a strong disambiguation signal for any system, human-built or AI-built, trying to work out which “Springfield” or which “Jordan” you mean.
JSON-LD isn’t always the right answer.
Google’s own documentation recommends JSON-LD as the preferred format, and for a lot of use cases that’s fair advice. But I think it gets applied too broadly. JSON-LD lives in a <script> tag, separate from the content it describes. For sitewide, contextual information — your WebSite, your Organization, your LocalBusiness — that separation is fine, even useful, because you’re describing things that exist independently of any single piece of visible content.
The problem shows up when you use JSON-LD for on-page content that already exists in the DOM. A menu, a blog post, an event listing, a product — the content is already there, visibly rendered. Duplicating it into a JSON-LD block means you’re now maintaining two representations of the same information. Someone edits the visible price on the menu page and forgets the JSON-LD block sitting in the <head>, and now your structured data is silently wrong. It also means shipping the same content twice in the page payload, which isn’t a big deal for a small site but adds up.
Microdata avoids this by attaching structured data directly to the content that’s already there — an itemprop="price" attribute doesn’t add a new price, it labels the existing one. There’s a single source of truth. Change the visible content and the structured data updates itself, because it is the structured data. For content that’s inherently page-specific — a menu, an event, a blog post, a recipe, a product page — that property alignment matters more than the tooling convenience JSON-LD offers.
There’s a genuine counterargument worth naming here, because it’s the reason Google leans towards JSON-LD in the first place: it’s much easier to inject via a tag manager without touching page templates. In large organisations where marketing controls GTM but doesn’t have commit access to the front end, that’s a decisive advantage, and JSON-LD wins on pragmatic grounds regardless of the duplication issue. If you own your templates directly, though, that advantage disappears, and Microdata’s tighter coupling to content starts to look like the better trade, and personally I think developers should be including this by default from the get-go.
One underused Microdata feature worth knowing about is itemref. Normally an item’s properties have to be DOM descendants of the element with itemscope, which doesn’t always match your markup structure — maybe your price sits in a different column, structurally unrelated to the item name. itemref lets you associate properties with a scope by ID reference instead of DOM nesting, which solves a lot of the “my structure doesn’t match Schema’s expectations” complaints people have about Microdata.
Where I land: mix them deliberately.
My rule of thumb is simple: JSON-LD for things that describe the site or organisation in general — WebSite, Organization, LocalBusiness, BreadcrumbList — and Microdata for things that describe specific, already-rendered on-page content — menus, events, blog posts, recipes, products.
The genuinely satisfying part is that you don’t have to choose one exclusively. You can link the two together. A JSON-LD LocalBusiness block can reference a Microdata-marked-up menu on the page using @id, and the Microdata item can point back using itemid, referencing the same identifier. Nest a Review inside a MenuItem. Reference an Organization from within an Event. The connections between entities are as valuable as the entities themselves — you’re not just saying “here’s a menu” and “here’s a restaurant”, you’re saying “here’s a menu, and it belongs to this specific restaurant, which is this specific entity, which has these reviews”. That web of explicit relationships is where structured data goes from useful to genuinely powerful.
<!-- JSON-LD in <head> -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Restaurant",
"@id": "https://fernlondon.co.uk/#restaurant",
"name": "Fern",
"hasMenu": {
"@type": "Menu",
"@id": "https://fernlondon.co.uk/menu/#menu"
}
}
</script>
<!-- Microdata in <body> linked via itemid -->
<main
itemscope
itemtype="https://schema.org/Menu"
itemid="https://fernlondon.co.uk/menu/#menu"
>
<section itemscope itemtype="https://schema.org/MenuSection">
<h2 itemprop="name">Starters</h2>
<div itemscope itemtype="https://schema.org/MenuItem">
<span itemprop="name">Charred Sourdough</span>
<div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
<span itemprop="price">6.50</span>
<meta itemprop="priceCurrency" content="GBP" />
</div>
</div>
</section>
</main>
Restaurant links to the Menu via hasMenu, and the Microdata itemid matches that same @id — a deliberate one-way link.A practical note if you’re doing this properly: @id values need to be unique across the page, and if you’re nesting scopes with Microdata, watch for itemid collisions across different levels of nesting — it’s an easy mistake to make and a difficult one to debug.
If you want to see this in action rather than just read about it, this very article is nested with structured data too — run it through Schema.org’s Validator and you’ll see the nesting described above reflected in the scan.
The takeaway.
Structured data isn’t a checkbox for rich snippets — it’s an opportunity to make your content legible to machines without ambiguity. Treat JSON-LD and Microdata as tools with different strengths rather than a single “best practice” to follow uniformly, and use nesting and @id/itemid references to make the relationships between your content as explicit as the content itself. That’s the part most structured data advice skips entirely, and it’s the part that actually makes a difference.
