Nesting Schema Markup (@id and @graph)

The deep-dive on connecting JSON-LD entities with @id and @graph — stable identifiers, the two canonical node patterns (WebSite→Article→Author, Product→Offer→Organization), the three mistakes that break the graph, and the honest limits of what Google's docs confirm.

First published: Jul 2, 2026 · Last updated: Jul 21, 2026 · Advanced
demand #24 in Structured Data#53 in On-Page#243 in Technical SEO#330 on the site

Nesting connects your JSON-LD entities instead of dumping them as disconnected blocks. @id is a stable, unique URI (usually a canonical URL plus a #fragment) that names an entity so other entities can reference it — an Article's author pointing at a Person node, a Product's Offer pointing at an Organization/seller — rather than re-declaring the full entity everywhere. @graph is a keyword that bundles those connected entities into one script block. Google's docs confirm both nesting and individual items work, and that @id is how you tell Google two items are linked, but they never confirm whether @id resolves across pages — so the safe default is consistent @id values plus the complete entity graph on every page that needs it. Two patterns to learn: WebSite → WebPage → Article → Author for publishing, Product → Offer → Organization for ecommerce. Three ways it breaks or costs you: inconsistent/non-unique @id values, orphaned references (an @id cited but never declared — a semantic gap, not invalid JSON-LD), and duplicating a full entity's declaration everywhere instead of referencing it (a maintenance trap; same-entity repeats merge rather than literally break, but a genuinely conflicting reused @id is a real break). It's an entity-understanding tool, not a ranking or rich-result lever by itself.

TL;DR — Nesting connects JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. entities via @id (a stable, unique URI — usually a canonical URLHow search engines pick one canonical URL among duplicates and consolidate signals onto it. plus a #fragment) so you reference an entity instead of re-declaring it, and @graph (a keyword bundling connected entities into one script block). Google’s docs confirm both nesting and individual items work, and that you use @id to link related items — but they never confirm whether @id resolves across pages, so the safe default is consistent @id values plus the complete entity graph on every page that needs it. Learn two patterns: WebSite → WebPage → Article → Author (publishing) and Product → Offer → Organization/seller (ecommerce). Three failure modes to watch for: inconsistent/non-unique @id values, orphaned references (an @id cited but never declared — semantically incomplete, not invalid JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.), and duplicate full declarations of the same entity (a maintenance trap; same-entity repeats merge under JSON-LD’s node rules, but reusing one @id for genuinely different entities is a real conflict). It’s an entity-understanding tool — disambiguation and de-duplication — not a ranking or rich-result lever by itself.

Evidence for this claim JSON-LD supports connected nodes via nesting or @id references, allowing multiple related entities to form one graph. Scope: JSON-LD graph model. Confidence: high · Verified: W3C JSON-LD 1.1 Evidence for this claim Google requires structured data to represent visible page content and follow each feature's specific nesting and property guidelines; extra valid nodes do not create eligibility by themselves. Scope: Current Google structured-data general guidelines. Confidence: high · Verified: Google Search Central: Structured data general guidelines

Scope: this is the deep-dive, not the intro

This article assumes you already know what JSON-LD is and why it’s the recommended format — the schema markupSchema markup is code that uses the schema.org vocabulary to label what your content means so search engines can understand it and show rich results. It's most often written in JSON-LD, and it's not a direct ranking factor. page covers that, and it introduces @id/@graph at a glance. This page is the deferred deep-dive: the actual mechanics, the standard node patterns, and the specific mistakes that quietly break the graph. If you want the format basics or where to place the <script> block, JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. is the sibling for that.

@id — a stable identifier, not a fetchable URL

@id assigns a unique URI to a JSON-LD entity so other entities can point at it by reference. The convention — consistent across every credible source I’ve checked — is an absolute canonical URL plus a descriptive fragment:

"@id": "https://example.com/#organization"
"@id": "https://example.com/#website"
"@id": "https://example.com/team/jane-doe/#person"

Two things people get wrong here:

  • It doesn’t have to resolve. Per the JSON-LD spec, @id’s job is node identification — a unique, stable “name” for an entity — not fetchability. Using a fragment on your own domain is standard practice and doesn’t require that fragment URL to independently load as a page. Sitebulb’s node-identifiers guide frames it well: a node identifier is “a unique ‘name’ for an entity, which is publicly accessible and can be looked up or linked to.”
  • @id is not url. They’re different properties doing different jobs. @id identifies the node in the graph; url describes a real, fetchable page about the entity. An Organization can (and often should) have both — an @id of https://example.com/#organization and a url of https://example.com/.

And @id values are case-sensitive#Organization and #organization are two different entities. Pick a convention and never drift from it.

@graph — bundling connected entities into one block

@graph is a JSON-LD keyword that lets you put multiple top-level entities into one array inside a single <script type="application/ld+json"> block, with entities cross-referencing each other by @id:

{
  "@context": "https://schema.org",
  "@graph": [
    { "@type": "Organization", "@id": "https://example.com/#organization", "...": "..." },
    { "@type": "WebSite", "@id": "https://example.com/#website", "...": "..." },
    { "@type": "WebPage", "@id": "https://example.com/post/#webpage", "...": "..." }
  ]
}

@graph is a container, not a requirement. You can absolutely use separate <script> blocks for individual items — Google’s docs confirm both approaches work. The reason to reach for @graph is maintainability: once you have three or more cross-referencing entities, you write @context once at the top instead of repeating it in every block, and everything that references everything else lives in one place. This is the pattern Yoast uses in production — its schema architecture docs explain that a shared graph lets them “avoid having to duplicate or repeat shared properties, and to reduce the amount of code/processing/overhead required.”

Why nest at all — and what Google actually says

Declare each entity once and connect the graph with stable `@id` references instead of repeating full blocks.

One Organization identified as hash organization is referenced as publisher by the WebSite and Article. The WebPage belongs to the WebSite and is connected to the Article. Each entity is declared once, and the same stable at-id string is reused for every reference.

Google documents two structural options and confirms it understands both. From the General Structured Data Guidelines: “Google Search understands multiple items on a page, whether you nest the items or specify each item individually.” It defines them plainly — nesting is “when there is one main item, and additional items are grouped under the main item”, and individual items is “when each item is a separate block on the same page.”

The one paragraph that’s the closest thing to an official rationale for @id is this: “If there are items that are more helpful when they are linked together (for example, a recipe and a video), use @id in both the recipe and the video items to specify that the video is about the recipe on the page. If you didn’t link the items together, Google Search may not know that it can show the video as a Recipe rich result.” That’s the mechanism in Google’s own words — @id is how you tell Google two items belong together.

Worth an honest note: Google’s docs state this principle in prose but don’t ship a worked @graph+@id code sample demonstrating it — their code examples show a nested Recipe and two individual top-level items, neither of which actually cross-references by @id. The examples below fill that gap.

This connects directly to schema’s two jobs (the framing from the schema markupSchema markup is code that uses the schema.org vocabulary to label what your content means so search engines can understand it and show rich results. It's most often written in JSON-LD, and it's not a direct ranking factor. page): nesting well is squarely an entity-understanding move — disambiguation and avoiding contradictory/duplicated data — not a rich-result trigger on its own. Linking your Article cleanly to its Author doesn’t unlock a new SERP featureSERP features are any element on a search results page beyond the classic ten blue links — featured snippets, People Also Ask, knowledge panels, sitelinks, image and video packs, AI Overviews, and structured-data-driven rich results. Google documents that losing rich-result eligibility doesn't affect ranking; their SEO relevance is CTR (they redistribute clicks). Some are unlocked by markup; most are purely algorithmic.; it just makes the relationships unambiguous.

Pattern 1 — WebSite → WebPage → Article → Author

The canonical publishing pattern. One Organization (the publisher), one WebSite, then per-page a WebPage and the Article on it, with author and publisher resolved by @id instead of re-declared:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#organization",
      "name": "Example Media",
      "url": "https://example.com/",
      "logo": "https://example.com/logo.png",
      "sameAs": [
        "https://www.linkedin.com/company/example-media/",
        "https://en.wikipedia.org/wiki/Example_Media"
      ]
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#website",
      "url": "https://example.com/",
      "name": "Example Media",
      "publisher": { "@id": "https://example.com/#organization" }
    },
    {
      "@type": "WebPage",
      "@id": "https://example.com/nesting-schema/#webpage",
      "url": "https://example.com/nesting-schema/",
      "name": "Nesting Schema Markup",
      "isPartOf": { "@id": "https://example.com/#website" }
    },
    {
      "@type": "Person",
      "@id": "https://example.com/team/jane-doe/#person",
      "name": "Jane Doe",
      "url": "https://example.com/team/jane-doe/",
      "sameAs": ["https://www.linkedin.com/in/jane-doe/"]
    },
    {
      "@type": "Article",
      "@id": "https://example.com/nesting-schema/#article",
      "mainEntityOfPage": { "@id": "https://example.com/nesting-schema/#webpage" },
      "headline": "Nesting Schema Markup",
      "author": { "@id": "https://example.com/team/jane-doe/#person" },
      "publisher": { "@id": "https://example.com/#organization" }
    }
  ]
}

Note what’s happening: author and publisher are one-line references. The Person and Organization are declared once each. The Article hangs off the WebPage (mainEntityOfPage), which is isPartOf the WebSite, which is publisher-ed by the Organization. That’s a connected graph, not five islands.

Pattern 2 — Product → Offer → Organization (seller)

The ecommerce equivalent, and where nesting earns its keep at scale: instead of re-declaring your merchant Organization on every one of thousands of product pages, declare it once and reference it as the seller:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://shop.example.com/#organization",
      "name": "Example Shop",
      "url": "https://shop.example.com/",
      "logo": "https://shop.example.com/logo.png"
    },
    {
      "@type": "Product",
      "@id": "https://shop.example.com/widget/#product",
      "name": "Deluxe Widget",
      "sku": "WIDGET-001",
      "brand": { "@type": "Brand", "name": "Example" },
      "offers": {
        "@type": "Offer",
        "price": "29.99",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock",
        "seller": { "@id": "https://shop.example.com/#organization" }
      }
    }
  ]
}

The Offer’s seller points at the shared Organization node. Same seller entity, one declaration, referenced from every product. (For very large variant sets, the same shared-@id idea extends to a ProductGroup with variant Products — that’s a specialization of this pattern, not a different mechanism.)

The three mistakes that break the graph

This is the practical heart of an audit. When nesting “doesn’t work,” it’s almost always one of these three.

1. Inconsistent or non-unique @id values

The same entity given different @id strings across pages (or across a replatform/migration), so search engines and validators see them as unrelated entities instead of one. Or the opposite — reusing one @id for two genuinely different entities. The fix is a strict, documented convention (e.g. {base-url}/#organization, {page-url}/#webpage, {profile-url}/#person) applied mechanically. Case counts; #author#Author.

2. Orphaned references

An @id is referenced but never declared. You write "author": {"@id": "https://example.com/#person-jane"}, but no node anywhere in the tested document actually declares that @id with a @type and properties. Worth being precise here: a bare {"@id": "..."} object — what the JSON-LD spec calls a node reference (“a node object used to reference a node having only the @id key”) — is valid JSON-LD syntax on its own; it isn’t a parse error. The problem is semantic and consumer-specific: the intended connection — “this Article’s author is that Person” — never resolves to anything useful, so any feature or reader that needed the author’s actual name, URL, or sameAs data comes up empty. This is the sneaky one: validators often still parse each declared item without erroring, so nothing screams at you. You ship an incomplete graph and never notice. Momentic’s @id guide catalogs this as one of the most common real-world nesting failures.

3. Duplicate full declarations of the same entity

The opposite of orphaning: re-declaring the complete Organization/Person object inline on every page instead of referencing it once. Two different things can happen here, and it’s worth telling them apart:

  • Repeated declarations of the genuinely same entity. Per the JSON-LD spec, “the properties of a node in a graph may be spread among different node objects within a document. When that happens, the keys of the different node objects need to be merged to create the properties of the resulting node.” So repeating the same @id with the same (or compatible) data isn’t a parse failure — processors merge it. That doesn’t make it a good idea: it’s still what nesting exists to prevent, and it bloats markup and creates a maintenance trap — update your address or logo in one template, forget the other forty, and the entity now contradicts itself across the site. Both Ahrefs’ schema coverage and Schema App independently make the same “change one and forget the others” point.
  • The same @id reused for two actually different entities. This is the real break: two node objects share an identifier but describe conflicting things (a different name, a different logo, a different @type). That’s a semantic conflict, not a harmless merge — pick a distinct @id for each genuinely separate entity.

Declare once, reference everywhere — the maintenance case for doing so holds either way, even though a same-entity repeat won’t technically break the graph the way an orphaned reference or a genuinely conflicting @id will.

The unresolved question: does @id resolve across pages?

Here’s the honest limit that separates a real answer from the confident-but-wrong version. Google’s documentation never explicitly states whether @id references resolve across pages — e.g., a product page’s Offer.seller pointing at an Organization node that’s declared only on the homepage. No Google Search Central document uses “cross-page” or “across pages” in relation to @id.

The one nearby signal points toward self-contained pages: Google recommends, for duplicate contentThe same or very similar primary content reachable at more than one URL. There's no general duplicate content penalty — the real costs are possible signal dilution, the wrong URL getting chosen, and less-efficient crawling., “placing the same structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. on all page duplicates, not just on the canonical page” — which implies structured data is evaluated per page, not fetched from elsewhere. And there’s no on-the-record statement from a named Google or Bing rep (Mueller, Illyes, Splitt) specifically resolving the cross-page @id question that I could verify. Bing, for its part, has JSON-LD validation in Webmaster Tools but no published @id/@graph-specific guidance at all — a genuine documentation gap, not a position I’m going to invent for them.

So, as of July 2026, treat “Google follows @id links across pages” as an industry inference, not a confirmed behavior. The safe default — which Yoast, Momentic, and Schema App all converge on independently — is: keep @id values consistent for the same entity everywhere, but still output the complete entity graph on every page that needs it. Don’t assume Google will fetch a referenced entity from a different URL. Yoast’s production choice is exactly this: it emits the full graph on every page rather than relying on cross-page resolution.

How to validate nested schema

Two tools:

  • Rich Results Test — in practitioner testing as of July 2026, it parses @graph arrays and resolves @id references within the tested document: when entity A references B by @id and B is declared in the same graph, they show as connected; when the referenced @id is never declared in the tested markup, the tool still parses the declared items individually without erroring, but can’t show the connection. Google doesn’t document this UI behavior explicitly anywhere I could find, so treat it as observed, reproducible tool behavior rather than an official specification — and re-check it if the tool’s UI changes. That behavior is exactly why orphaned references slip through — no error, just a missing link.
  • Schema Markup Validatorschema.orgSchema markup is code that uses the schema.org vocabulary to label what your content means so search engines can understand it and show rich results. It's most often written in JSON-LD, and it's not a direct ranking factor.’s own vocabulary validator, less opinionated about Google’s supported rich-result types.

Test the rendered page, not just your template source — if JSON-LD is injected via JavaScript, confirm it’s actually present. Google documents reading JSON-LD that’s dynamically injected by JavaScript, but that’s specific to Google — JavaScript-execution behavior varies by crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. and product, and there’s no blanket rule that every AI crawlerAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. renders JS the same way (or at all). Shipping JSON-LD in the initial HTML rather than assuming it’ll be executed is the conservative choice — that’s a schema-markup-for-AISchema markup (structured data) is machine-readable code — usually JSON-LD — that labels what your content means using the schema.org vocabulary. For AI search it's infrastructure for entity disambiguation, not a direct citation lever: controlled studies found no meaningful uplift in AI citations from adding it. concern.

Myths worth killing

  • @id needs to be a real, live, fetchable URL.” No — by convention it’s a canonical URL plus a fragment, but its function is identification, not fetchability. The fragment doesn’t need to resolve to its own page.
  • “Same @id on different pages auto-merges those entities in Google’s indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., like a canonical tagA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. consolidates pages.” Not confirmed by any Google doc. Consistent @id is good hygiene, not a proven cross-page consolidation mechanism.
  • “You must use @graph; separate script blocks are wrong.” False. Google understands both. @graph is a maintainability choice.
  • “More nesting always improves entity understanding.” Only if the entities are genuinely related. Nesting unrelated items (Schema App’s example: an unrelated Event under a Recipe) doesn’t help and can muddy signals.
  • @id/@graph structure is a ranking or rich-result factor.” No. Its value is disambiguation and reduced duplication — consistent with schema not being a ranking factor at all.
  • “An undeclared @id is harmless; engines just ignore it.” That’s the orphaned-reference failure — the intended connection silently doesn’t form, and tools often don’t error, so you ship it unnoticed.

Where this sits

This is the how-to layer under the structured data hubStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding., a deeper sibling of schema markupSchema markup is code that uses the schema.org vocabulary to label what your content means so search engines can understand it and show rich results. It's most often written in JSON-LD, and it's not a direct ranking factor. (which introduces @id/@graph) and JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. (the format). It leans on the same entity-understanding thinking as entities\"Entity & identity schema\" is a practitioner label — not an official Google or schema.org category, and not exhaustive of identity-capable schema.org types — for the three main types that declare who or what is behind a site: Organization, LocalBusiness, and Person. and schema markup for AISchema markup (structured data) is machine-readable code — usually JSON-LD — that labels what your content means using the schema.org vocabulary. For AI search it's infrastructure for entity disambiguation, not a direct citation lever: controlled studies found no meaningful uplift in AI citations from adding it. — a well-built graph is entity infrastructure, whether the consumer is Google’s Knowledge GraphThe Knowledge Graph is Google's database of entities — people, places, organizations, and things — and the factual relationships between them. It's separate from any single website's structured data: your schema markup is one of many possible inputs to the graph, not the graph itself. or an LLM. The whole sub-cluster lives inside on-page SEO.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.