DEV Community

Joseph Anady
Joseph Anady

Posted on • Originally published at thatdevpro.com

Building a Schema.org @graph That Validates on the First Try

Most agencies hand you a Schema.org JSON-LD block that fails the Rich Results Test on the first try. Duplicate @id values, orphaned references, mismatched types, missing required fields. The pattern that actually validates is a single @graph block with explicit @id threading.

Short answer: Use a single JSON-LD script tag with @graph as the top-level key. Give every node an explicit @id. Cross-reference nodes by @id using the {"@id": "..."} shortcut.

Why multiple JSON-LD blocks fall apart

Search engines do not collate multiple JSON-LD blocks the way you might expect. Each block is parsed independently. When two blocks define an Organization with the same canonical URL but different name values, Google does not merge them. It picks one and discards the other nondeterministically.

The fix is structural: one block, one graph, every entity addressable by @id.

The shape that always validates

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": ["Organization", "ProfessionalService"],
      "@id": "https://example.com/#organization",
      "name": "Example Studio",
      "url": "https://example.com/",
      "logo": "https://example.com/logo.svg",
      "founder": { "@id": "https://example.com/#founder" },
      "sameAs": [
        "https://www.linkedin.com/company/example-studio",
        "https://x.com/examplestudio"
      ]
    },
    {
      "@type": "Person",
      "@id": "https://example.com/#founder",
      "name": "Jane Doe",
      "worksFor": { "@id": "https://example.com/#organization" }
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#website",
      "url": "https://example.com/",
      "publisher": { "@id": "https://example.com/#organization" }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Three nodes, three @id values, two cross-references. Google parses this as a single connected graph.

Common mistakes that break the graph

Duplicate @id collisions. If two nodes share the same @id, Google merges them and may keep only one.

Orphaned cross-references. If you reference an @id that does not exist in the graph, the cross-reference silently drops.

Missing required fields. LocalBusiness needs address, telephone, and geo or Place reference. Organization needs name and url at minimum.

Per-page extension pattern

The graph above lives in the site shell. On individual pages, add a second JSON-LD block that extends the graph with page-specific entities.

{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "@id": "https://example.com/blog/post-slug/#post",
  "headline": "Post title",
  "author": { "@id": "https://example.com/#founder" },
  "publisher": { "@id": "https://example.com/#organization" },
  "datePublished": "2026-05-22T08:00:00-05:00"
}
Enter fullscreen mode Exit fullscreen mode

The BlogPosting does not redefine author or publisher. It points at the existing graph nodes by @id.

Validation workflow

  1. Paste the rendered HTML into Google Rich Results Test
  2. Fix field-level errors (red items) first
  3. Then fix warnings (yellow items)
  4. Confirm eligible rich result types
  5. Re-test after every JSON-LD change in production

What this earns you

A clean @graph that validates on the first try is the foundation of every rich result Google ranks for: Organization Knowledge Panels, Person Knowledge Panels, FAQ rich snippets, Breadcrumb rich results, LocalBusiness map cards.

If you want this done correctly the first time, ThatDevPro runs schema markup engineering as a standalone service. Or read the research thread on schema graph completeness.

For the full version with extended examples and validation case studies, see the original at thatdevpro.com.


Posted from ThatDevPro, SDVOSB veteran-owned web development and AI engineering studio. Schema graph audits available via ThatDeveloperGuy.

Top comments (0)