@id Referencing in Structured Data
Structured data is not just a collection of isolated schema blocks scattered across a page. At its best, it forms a
connected entity graph — a machine-readable picture of how the things described on your page and
across your site relate to one another. This module teaches you the two mechanisms that make that graph possible:
nesting and @id referencing.
By the end of this module you will understand not only the syntax but the architectural reasoning behind these patterns, and you will be able to hand-author a fully connected JSON-LD block that validates cleanly and communicates rich entity relationships to search engines.
Google does not just read keywords — it builds a model of entities (things with identity) and the relationships between them. When your structured data expresses those relationships explicitly, you are giving the engine a shortcut: instead of inferring that the brand of this product is the same organization as the site owner, you assert it directly.
A flat list of schema blocks — one for Product, a separate one for Organization,
another for BreadcrumbList — describes three independent facts. A connected graph with nesting and
@id links describes a network of related facts. The latter is significantly more valuable
signal for Knowledge Graph reinforcement, entity disambiguation, and rich-result eligibility.
JSON-LD (JSON for Linked Data) is the format Google recommends for structured data. Before getting into nesting, make sure these core concepts are solid:
@context — declares the vocabulary. Always "https://schema.org" for SEO purposes.@type — declares what kind of thing is being described (e.g., Product, Organization).@id — a URI that uniquely and persistently identifies a specific entity. Think of it as the entity's permanent address in the graph.@graph — an array that lets you define multiple top-level entities inside a single <script> block.Nesting means placing one schema type directly inside a property of a parent type. The child entity becomes an inline description of a property value rather than a separate top-level object.
The most common example is a Product that contains an Offer, which itself contains an
AggregateRating and a Brand:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Noise-Cancelling Headphones Pro",
"image": "https://example.com/images/headphones-pro.jpg",
"description": "Professional-grade noise-cancelling headphones.",
"brand": {
"@type": "Brand",
"name": "SoundCore"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"reviewCount": "312"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/products/headphones-pro",
"priceCurrency": "USD",
"price": "249.99",
"availability": "https://schema.org/InStock",
"seller": {
"@type": "Organization",
"name": "Example Store"
}
}
}
Notice how brand, aggregateRating, offers, and seller each
have their own @type. This is nesting. The child types inherit context from their parent and create a
hierarchical, richly described object.
offers,
brand, author, etc.) defines the semantic edge between parent and child.
Do not invent property names.
Brand appears
on hundreds of product pages, you want to reference it (with @id), not re-describe it
on every page.
@id references.
@type should be a valid Schema.org type. Validate at each level — not
just the root — using the Schema Markup Validator.
@id: Giving Entities a Persistent Identity
The @id property assigns a stable, globally unique identifier to an entity. In practice, this is
always a URL — either a real page URL, a fragment URL, or a canonical URL representing the entity's
"home" in your data model.
Once an entity has an @id, any other entity anywhere on the site can reference it
by that identifier — without repeating all its properties. The reference is simply an object containing only the
@id:
{ "@id": "https://example.com/#organization" }
A parser or search engine that has already seen the full definition of
https://example.com/#organization knows that this reference points to that same entity.
@graph
The canonical pattern is to use a single @graph array in one <script type="application/ld+json">
block. This allows you to define entities (with all their properties) and then
cross-reference them within the same document.
Here is the complete structure for a product page, demonstrating both nesting and @id referencing:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://example.com/#organization",
"name": "Example Store",
"url": "https://example.com",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
},
"sameAs": [
"https://twitter.com/examplestore",
"https://www.facebook.com/examplestore"
]
},
{
"@type": "WebSite",
"@id": "https://example.com/#website",
"url": "https://example.com",
"name": "Example Store",
"publisher": { "@id": "https://example.com/#organization" }
},
{
"@type": "BreadcrumbList",
"@id": "https://example.com/products/headphones-pro#breadcrumb",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://example.com"
},
{
"@type": "ListItem",
"position": 2,
"name": "Headphones",
"item": "https://example.com/headphones"
},
{
"@type": "ListItem",
"position": 3,
"name": "Noise-Cancelling Headphones Pro",
"item": "https://example.com/products/headphones-pro"
}
]
},
{
"@type": "Product",
"@id": "https://example.com/products/headphones-pro#product",
"name": "Noise-Cancelling Headphones Pro",
"image": "https://example.com/images/headphones-pro.jpg",
"description": "Professional-grade noise-cancelling headphones for audiophiles.",
"sku": "NCH-PRO-001",
"brand": {
"@type": "Brand",
"name": "SoundCore"
},
"manufacturer": { "@id": "https://example.com/#organization" },
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"bestRating": "5",
"reviewCount": "312"
},
"review": [
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": "5",
"bestRating": "5"
},
"author": {
"@type": "Person",
"name": "Jordan M."
},
"reviewBody": "Exceptional noise cancellation, incredibly comfortable."
}
],
"offers": {
"@type": "Offer",
"url": "https://example.com/products/headphones-pro",
"priceCurrency": "USD",
"price": "249.99",
"priceValidUntil": "2025-12-31",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"seller": { "@id": "https://example.com/#organization" }
}
},
{
"@type": "WebPage",
"@id": "https://example.com/products/headphones-pro#webpage",
"url": "https://example.com/products/headphones-pro",
"name": "Noise-Cancelling Headphones Pro — Example Store",
"isPartOf": { "@id": "https://example.com/#website" },
"primaryImageOfPage": {
"@type": "ImageObject",
"url": "https://example.com/images/headphones-pro.jpg"
},
"breadcrumb": { "@id": "https://example.com/products/headphones-pro#breadcrumb" },
"about": { "@id": "https://example.com/products/headphones-pro#product" }
}
]
}
</script>
Let us break down the key decisions in the block above:
Organization with @id — Defined once, site-wide (ideally on the
homepage and included on every page). All other types that involve the organization — seller,
publisher, manufacturer — reference it by @id rather than repeating
its properties. This is how you build a coherent site-level entity.
WebSite with publisher referencing Organization — Connects
the site entity to its owning organization. The publisher value is simply
{ "@id": "https://example.com/#organization" }.
BreadcrumbList with its own @id — Allows the WebPage to
reference the breadcrumb directly rather than embedding it inline, keeping the WebPage block clean.
Product with nested aggregateRating, review, and
offers — These sub-types are tightly scoped to this specific product, so nesting is
appropriate. They do not have their own persistent identity across the site.
Offer.seller referencing Organization — Instead of duplicating the
organization's name and URL inside the offer, a pointer to the already-defined entity is sufficient.
WebPage tying everything together — isPartOf links to the
WebSite, breadcrumb links to the BreadcrumbList, and about
links to the Product. The page entity acts as a hub.
The decision rule is straightforward:
Offer, an AggregateRating, a specific Review). These entities
do not meaningfully exist outside the context of their parent.
@id when the child entity has an identity of its own
that persists across pages or across the site (an Organization, an Author,
a Brand that applies to many products). Define it fully once; reference it everywhere else.
When in doubt, ask: "If I move to a different page on this site, does this entity still exist as the same
thing?" If yes, it deserves an @id.
@id URIs Consistently
The value of @id must be a URI. It does not have to resolve to a real page, but it must be
stable, unique, and consistent across your entire implementation. The recommended conventions are:
https://example.com/#organization, https://example.com/#website.
https://example.com/products/headphones-pro#product,
https://example.com/products/headphones-pro#webpage.
#product, #breadcrumb, #webpage — not a mix of styles.
@id once it is published if you intend it to be stable. Changing
it is equivalent to deleting the old entity and creating a new one.
The power of @id extends beyond a single page. When the same
@id value appears in schema blocks across multiple pages, parsers and search engines can
merge those descriptions into a unified entity profile.
For example, if every product page on your site includes:
"seller": { "@id": "https://example.com/#organization" }
…and your homepage defines https://example.com/#organization in full (with name,
url, logo, sameAs, contact info, etc.), Google can associate the full
organizational profile with every seller reference across the entire catalog. You are contributing incrementally
to an entity's description each time it appears — which compounds into a stronger Knowledge Graph signal over time.
This is the core mechanism behind entity SEO: consistent, cross-page use of stable
@id URIs that resolve to the same entity is how you build machine-readable authority.
@id values. Using https://example.com/#Organization
on one page and https://example.com/#organization on another creates two separate entities.
URIs are case-sensitive.
{ "@id": "https://example.com/#organization" } on every page but never define the full entity
(with @type, name, etc.) anywhere, parsers have nothing to resolve the reference to.
The definition must exist somewhere — typically on the homepage or in a sitewide injection.
Organization block on every product page defeats the purpose of cross-page entity building.
Offer
shows a price that differs from what is displayed on the page, Google may ignore the schema or issue a manual
action. Every nested property must match its visible counterpart.
seller, manufacturer, and
brand expect specific types. Validate thoroughly — not just the root type, but every nested child.
@id URLs. If your product page canonical is
https://example.com/products/headphones-pro, the #product @id should
be built from that canonical, not from a staging URL or a URL with parameters.
After authoring a nested, cross-referenced JSON-LD block, validate it rigorously:
validator.schema.org) — checks syntactic and semantic
validity against the Schema.org specification. Use this first.
search.google.com/test/rich-results) — checks eligibility for
Google's specific rich-result features. A valid schema block does not automatically earn a rich result; this
tool shows which Google-specific requirements are met.
Hand-authoring this block on one product page is a learning exercise. In production, it must be generated programmatically from your CMS data. The architecture decisions you make here carry directly into Phase 4:
wp_head hook).
@id URIs should be constructed from the page's canonical URL — which itself should come from
the CMS. Never construct @id from window.location client-side; it must be present
in the server-rendered HTML.
generateMetadata-adjacent function, serialize it, and output it via a
<script type="application/ld+json"> tag — confirming with View Source that it is present
in the raw HTML before JavaScript runs.
Before moving on, complete the following exercise without looking at any pre-built examples:
@graph-based JSON-LD block from scratch,
including: Organization (with @id and sameAs), WebSite
(referencing Organization), BreadcrumbList (with at least three levels),
Product (with nested Brand, AggregateRating, at least one
Review, and an Offer whose seller references the
Organization @id), and a WebPage that references the breadcrumb and
the product.
@id you used and write a one-sentence justification for why each entity deserved a
persistent identifier.
You have completed this module when you can:
@id referencing, and correctly choose between them
for any given entity relationship.
@id naming convention and apply it consistently across page types.
@graph JSON-LD block for a product page that
validates clean in the Schema Markup Validator and passes the Rich Results Test.
@id referencing contributes to entity authority in the Knowledge Graph.