Understanding @graph in Schema.org JSON-LD

Text on a colourful background. Text says Using @graph in JSON-LD

When you mark up a page with Schema.org JSON-LD, you often describe one main entity per script tag (for example, an Article or Product). But pages frequently contain multiple pieces of structured data, say, information about an Organization, a WebSite, and a WebPage all on the same page.

You could nest one schema inside another (e.g. an Organization inside a WebSite property), or simply use separate <script type="application/ld+json" blocks for each entity. However, the JSON-LD @graph keyword offers a third option: it lets you group multiple “top-level” nodes in a single script.

In effect, @graph creates one JSON-LD script that contains an array of entity objects, linked together if needed by their @id references.

Using @graph is fully supported by search engines (Google and Bing). Think of it as a way to provide a list/array of entities at the top level of a single script tag. In practice, you put one "@context": "https://schema.org" at the top, then an "@graph": [ … ] array. Each item in that array is a schema object (with its own @type, properties, etc.).

This flat structure can simplify things: all entities live side by side in @graph, and they can reference each other by @id. In short, @graph means “these nodes belong together in one structured-data graph” instead of scattering them across multiple scripts or nesting one inside another.

Examples: @graph vs. Nesting vs. Multiple Scripts

Consider a page for Example Company. You want to mark up both the organization and the website. Here are three ways:

1. Nesting

Nest the Organization inside the publisher of the WebSite (or vice versa).

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "url": "https://example.com/",
  "name": "Example Site",
  "publisher": {
    "@type": "Organization",
    "name": "Example Company",
    "url": "https://example.com/"
  }
}
</script>

This embeds the Organization object as a property of WebSite. It works well when one entity naturally belongs inside another. But if both entities are independently important (e.g. multiple products or blog posts on the same page), this can get messy or impossible.

2. Multiple Scripts

Use separate script tags for each entity.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "url": "https://example.com/",
  "name": "Example Site",
  "publisher": { "@id": "https://example.com/#org" }
}
</script>
<script type="application/ld+json">    
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://example.com/#org",
  "name": "Example Company",
  "url": "https://example.com/"
}
</script>

Each block stands alone. Google and other parsers will read both and effectively merge them (especially if you use @id links). Multiple >script> tags can be placed on a webpage, where the combination of all pieces from all >script> tags should result in one connected graph of schema data”The downside is repetition (each script has its own @context) and possibly more files to maintain.

3. Consolidate Into One JSON-LD Graph

Combine both entities into one script using @graph.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#org",
      "name": "Example Company",
      "url": "https://example.com/"
    },
    {
      "@type": "WebSite",
      "url": "https://example.com/",
      "name": "Example Site",
      "publisher": { "@id": "https://example.com/#org" }
    }
  ]
}              
</script>

Here we have one script. Notice we only declare "@context" once, at the top. Inside "@graph", we have an array of two objects: one for the Organization, and one for the WebSite. The WebSite refers to the Organization via its @id. This flat, array-based structure is exactly what JSON-LD’s graph syntax is for. It allows multiple “root” nodes in one document. As the JSON-LD spec notes, using a top-level map with @graph can “be useful for saving the repetition of @context” when describing disconnected nodes. In effect, you get the clarity of separate entities without needing separate scripts.

Notably, using @graph differs from simply wrapping multiple objects in a JSON array without "@graph". While you can combine objects in an array in one <script>, each object typically would repeat its own "@context". The @graph approach avoids that redundancy. In practice, web developers often find it cleaner to have one context and one graph array containing all their entities, rather than multiple contexts or entirely separate scripts.

When and Why to Use @graph

Using @graph really shines when you have multiple schemas that are related or should be maintained together. For example: an Article page that also needs an Author, Publisher, and perhaps Organization data. With @graph, you can put each schema object side by side, linking them by IDs. It becomes easy to add or update just one piece without rewriting the others. As one schema expert explains, a flat @graph lets you have “all the entities directly in the @graph then link to each other using their @id values. This is a good structure if you want to have more than one link to a specific entity. It also makes it easier to add or modify entities.” In other words, if multiple parts of your site need to point to the same Organization or Person, giving that entity an @id in a shared graph means you can reference it cleanly.

Another scenario is dynamic or component-based sites. For instance, on a product page you might have a base script for the Organization and WebSite (common across the site) and then a product-specific script. Yoast’s schema approach does exactly this: it outputs a “base script” with a @graph of core entities on every page, then adds or modifies graph entries per page type. If you instead used separate scripts, you might have to duplicate the base info in each script or rely on consistent IDs to merge them. Using one graph script simplifies maintenance: the core markup and page-specific markup live in one place in the page’s HTML.

In short, use @graph when you want one unified block of JSON-LD that covers several entity types. It helps avoid context repetition and can mirror how your back-end or plugin already organizes data. On the other hand, if there’s only one entity, or if entities are naturally nested, then a single object (no @graph) is fine. If your site’s architecture already outputs different pieces of JSON-LD independently (as separate scripts), that is also valid. Google’s docs emphasize ease of implementation: all JSON-LD on a page is effectively merged during crawling. One StackOverflow answer points out that there is “no benefit in having single or multiple data blocks, other than limitations around how you might store and manage schema data in your website. In other words, choose the approach that best fits your codebase. But if you can manage everything in one place, a single @graph script often makes sense.

Best Practices and Tips

When using @graph, follow some best practices:

  • Declare Context Once: Put "@context": "https://schema.org" at the top-level of the script (outside the @graph array). Do not repeat context inside each graph item. In fact, within @graph, each item should omit @context.
  • Use Stable @id URLs: Whenever possible, give each entity an @id URI (even if just a page-fragment anchor, like example.com/#org). This allows other graph items to point to it. Consistent IDs help search engines understand when two JSON-LD entries refer to the same thing. In our examples above, the WebSite item had "publisher": { "@id": "https://example.com/#org" } and the Organization item had that same @id. This makes the relationship explicit.
  • Flat vs Nested: If entities are truly related by a simple property (e.g. an author inside a Book), nesting might be easier. But if they are separate (like product and review data, or a site section and the site itself), a flat graph is often cleaner. Don’t force nesting if it doesn’t semantically fit.
  • Validation: Always validate your JSON-LD. Google’s Rich Results Test or Schema.org’s Structured Data Testing Tool will catch syntax issues or missing contexts. For example, when using @graph, make sure your JSON is still a single valid object (with one top-level { ... }). Missing commas or braces inside the graph array will break the script.
  • Keep It Focused and Accurate: Whether you use @graph or not, only include schema that reflects visible content. Google’s guidelines state not to markup irrelevant info. In other words, don’t stuff hidden schemas into your graph just to boost SEO. A well-organised graph makes the structured data easier to audit. If you find duplicate or conflicting entries (e.g. two separate WebSite nodes), consolidate them.

SEO and Implementation Considerations

From an SEO perspective, using @graph has no inherent penalty or special boost – it’s simply a way of organising the data. Search engines parse JSON-LD markup from all <script type="application/ld+json"> tags on a page and effectively merge them. As noted, if you use multiple scripts or @graph, Google will combine them into a single understanding (especially if @ids match). In practice, this means: whether you put related entities in one script or split them, Google will see the full set of entities.

Google supports @graph… Without @graph you need to start with one entity and link to the others from it. Or put them all in separate script tags. This indicates that using @graph is as acceptable as multiple scripts. There is no known “SEO penalty” for having multiple scripts; one GitHub issue mentions fears of “SEO penalties for multiple script tags,” but this is not documented by Google and is generally considered unfounded. In fact, standard practice (e.g. Yoast SEO) happily uses multiple scripts or a graph interchangeably. The key is simply that the structured data is valid and correctly represents the content.

In terms of page performance or complexity, having one script or many has negligible difference – JSON-LD data is usually small. On pages with many schema types (articles, FAQs, products, etc.), consolidating them can make the HTML a bit cleaner. But it also means a single large JSON blob which could be slightly harder to debug if there’s a syntax error. Some developers prefer separate scripts so that one malformed block doesn’t invalidate the others. This is a trade-off: multiple blocks isolate problems, but one graph is a single point of failure if it’s wrong. Use whichever fits your development style, but always test.

Finally, consider tooling and CMS support. Some frameworks or plugins already support @graph. For instance, as mentioned, Yoast SEO’s Schema implementation is built around a base graph. If your tooling outputs JSON-LD pieces with consistent IDs, merging with @graph might be straightforward. If not, you could always combine them at build-time or template-time. The main takeaway: structure your data so that it’s easy to maintain, and know that @graph is just an organisational tool with full support by major search engines.

By using @graph wisely, you can keep related schema entities together, reduce repetition, and make your site’s structured data cleaner. Remember to keep everything validated, linked with IDs if needed, and aligned with your on-page content. Whether you choose @graph, nesting, or multiple scripts, the goal is the same: help search engines understand the content. In many cases, @graph simply makes that job (and yours) easier, without any downside.