TechnicalSchema

JSON-LD Explained: Syntax, @id, @graph, Examples

JSON-LD (JavaScript Object Notation for Linked Data) is a way of writing structured data as JSON so that machines know exactly what each value means. On websites, it sits in a <script type="application/ld+json"> tag and describes the page’s content (an article, a product, a business, a person) using the schema.org vocabulary. It’s the format Google recommends for structured data, and four keywords (@context, @type, @id and @graph) do almost all the work.

This guide is for marketers and developers who can already copy a snippet but want to understand it well enough to write one, fix one, or connect several into a graph. It covers the syntax, the difference between nesting and referencing, where the script goes, and a complete example.

On this page

What is JSON-LD used for?

It’s used to publish structured data: explicit statements about the things a page describes. The JSON-LD project site calls it “a lightweight Linked Data format” that is easy for humans to read and write, and the W3C published JSON-LD 1.1 as a Recommendation on July 16, 2020. Adoption is still climbing: the HTTP Archive’s 2024 Web Almanac found JSON-LD on 41% of pages, up from 34% in 2022, with WebSite the most common type (12.73% of mobile pages), followed by Organization (7.16%).

In SEO, it does three jobs:

  • Tells search engines what the page is. Google’s introduction to structured data describes it as “a standardized format for providing information about a page and classifying the page content”.
  • Makes pages eligible for rich results such as review stars, product prices, event listings and breadcrumbs. Google’s introduction cites publishers that measured the effect: Food Network converted 80% of its pages to enable search features and saw a 35% increase in visits, and Rakuten found users spent 1.5x more time on pages with structured data.
  • Declares entity facts about your organization, people and services, which helps engines, including AI assistants, describe you correctly. Our guide to structured data for AI search covers which types matter for that.

It isn’t a ranking lever on its own. Google’s general structured data guidelines say a structured data manual action removes rich result eligibility but “doesn’t affect how the page ranks in Google web search”.

JSON vs JSON-LD: what’s the difference?

JSON is a data format; JSON-LD is JSON with agreed meaning. A plain JSON object with a key called name could mean anything. In JSON-LD, the @context maps name to a defined term in a vocabulary, so every consumer reads it the same way.

The practical differences:

  • Every document is valid JSON. Any JSON parser can read it. Break the JSON (a trailing comma, a smart quote) and you break the linked data too.
  • It adds keywords that start with @. They’re reserved, and you can’t invent new ones.
  • It gives things identity. With @id, two blocks on two different pages can talk about the same organization.

The diagram below labels each part of a small block.

Anatomy of a JSON-LD block, with each keyword labelled A code panel on the left shows a script tag of type application/ld+json containing: @context https://schema.org, @type Organization, @id https://example.com/#org, name Example Co, url https://example.com/, and sameAs with one profile URL. Labels on the right explain each line. The script tag tells the browser this is data, not code to run. @context says which vocabulary the keys come from. @type says what kind of thing this is. @id gives the thing a permanent identifier that other nodes can reference. name, url and sameAs are ordinary schema.org properties. Anatomy of a JSON-LD block Keywords start with @; everything else is a schema.org property <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Organization", "@id": "https://example.com/#org", "name": "Example Co", "url": "https://example.com/", "sameAs": ["https://www.linkedin.com/..."] } </script> Script tag: data, not code to run @context: which vocabulary the keys use @type: what kind of thing this is @id: permanent identifier others can point to Properties: plain schema.org terms

Scroll the diagram sideways to see all of it.

Source: Ambeltek diagram based on the keyword definitions in the W3C JSON-LD 1.1 specification.

The four keywords: @context, @type, @id and @graph

Four keywords cover nearly every SEO use of the format. The definitions below come from the JSON-LD 1.1 specification.

  • @context is “used to define the short-hand names that are used throughout a JSON-LD document”. For SEO it’s almost always "https://schema.org", which is why "name" means schema.org’s name.
  • @type is “used to set the type of a node”. It’s the schema.org type: Organization, BlogPosting, Product, Event. Use the most specific type that’s true; Google’s guidelines ask for “the most specific applicable type and property names defined by schema.org”.
  • @id is “used to uniquely identify node objects”. Give important entities a stable URL-based identifier, like https://example.com/#organization, and never change it.
  • @graph is “used to express a graph”. It holds an array of separate nodes in one script, so you can describe the organization, the website, the page and the article side by side, each with its own @id.

Three rules keep these keywords working. @id values are identifiers, not links, so they don’t have to resolve to a page, but using your real domain avoids collisions with anyone else’s. The same entity must use the identical @id string everywhere, down to https and trailing slashes. And @type can take an array when something is genuinely two things, such as a Product that is also a Book.

Nesting vs referencing

There are two ways to connect things: put one object inside another (nesting), or give it an @id and point to that (referencing). Both are valid. Google’s guidelines say it “understands multiple items on a page, whether you nest the items or specify each item individually”, and recommend @id when separate items belong together.

The diagram below compares the two approaches.

Nesting versus referencing the same publisher in JSON-LD Two panels side by side. Left panel, nesting: a BlogPosting contains a full publisher object with @type Organization, name and logo written inline. Pros: self-contained and easy to read. Cons: the organization is re-described on every page and copies drift apart. Right panel, referencing: a BlogPosting has publisher set to only @id https://example.com/#org, and a separate Organization node declared once in @graph holds the name, logo and sameAs. Pros: one source of truth, reused site-wide. Cons: a typo in the @id silently creates a second entity. A bottom note recommends nesting for small, page-specific objects such as an Offer or a PostalAddress, and referencing for entities you reuse, such as your organization, people and website. Nesting vs referencing: the same publisher, two ways Both are valid; they fail in different ways Nesting "@type": "BlogPosting", "publisher": { "@type": "Organization", "name": "Example Co", "logo": "..." } + self-contained, easy to read - re-described on every page; copies drift Referencing "@type": "BlogPosting", "publisher": {"@id": "https://example.com/#org"} Organization declared once, with name, logo and sameAs, under "@id": "https://example.com/#org" + one source of truth, reused site-wide - an @id typo quietly makes a second entity Rule of thumb: nest small, page-only objects (Offer, PostalAddress, Rating). Reference entities you reuse: your organization, your people, your website.

Scroll the diagram sideways to see all of it.

Source: Ambeltek diagram based on Google's general structured data guidelines (multiple items on a page) and the JSON-LD 1.1 specification.

Referencing is what turns a site’s markup into a graph. Your organization is described once, with its logo, address and sameAs profiles, and every article, service and page points to it. That’s the pattern behind Organization schema for AI search and Person schema for author entities, and it’s why a clean sameAs list only needs to live in one place (see the sameAs guide).

A referenced node doesn’t have to be on the same page to be meaningful as an identifier, but Google’s guidelines also say to put structured data on the page it describes. The safe pattern is to declare your core Organization and WebSite nodes in a site-wide template so they’re present on every page, and reference them from page-level nodes.

Where does JSON-LD go on a page?

The markup goes in a <script type="application/ld+json"> element, in the <head> or the <body>. Google’s introduction says it is embedded “in the <head> and <body> elements of an HTML page”, and that because the markup isn’t interleaved with visible text, nested data is easier to express.

How the script gets there matters more than where it sits:

  • Server-rendered (best). The script is in the HTML the server sends. Every crawler sees it on the first request. Static site generators and server frameworks do this naturally; our Astro SEO guide shows one setup.
  • Injected by JavaScript (works for Google). Google says it can read JSON-LD “dynamically injected into the page’s contents, such as by JavaScript code or embedded widgets”. Many AI crawlers don’t run JavaScript, though, as covered in JavaScript rendering and AI crawlers.
  • Added by a tag manager (use carefully). Google’s JavaScript structured data guide warns that duplicating page information in Google Tag Manager “increases the risk of having a mismatch between page content and the structured data”.

For product pages there’s an extra reason to render on the server. Google’s product documentation warns that JavaScript-generated Product markup can make Shopping crawls “less frequent and less reliable” for fast-changing prices and availability.

A complete example graph

Here’s a full @graph for a blog post page: the organization, the website, the web page, the article, its author and its breadcrumb, all connected by @id. Replace the example values with your own, and only include properties that are true and visible on the page.

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#organization",
      "name": "Example Co",
      "url": "https://example.com/",
      "logo": "https://example.com/logo.png"
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#website",
      "url": "https://example.com/",
      "name": "Example Co",
      "publisher": { "@id": "https://example.com/#organization" }
    },
    {
      "@type": "Person",
      "@id": "https://example.com/about/jane-doe/#person",
      "name": "Jane Doe",
      "url": "https://example.com/about/jane-doe/"
    },
    {
      "@type": "WebPage",
      "@id": "https://example.com/blog/json-ld-guide/#webpage",
      "url": "https://example.com/blog/json-ld-guide/",
      "isPartOf": { "@id": "https://example.com/#website" },
      "breadcrumb": { "@id": "https://example.com/blog/json-ld-guide/#breadcrumb" }
    },
    {
      "@type": "BlogPosting",
      "@id": "https://example.com/blog/json-ld-guide/#article",
      "mainEntityOfPage": { "@id": "https://example.com/blog/json-ld-guide/#webpage" },
      "headline": "A practical guide to JSON-LD",
      "datePublished": "2026-09-22T09:00:00-04:00",
      "dateModified": "2026-09-22T09:00:00-04:00",
      "image": ["https://example.com/images/json-ld-16x9.jpg"],
      "author": { "@id": "https://example.com/about/jane-doe/#person" },
      "publisher": { "@id": "https://example.com/#organization" }
    },
    {
      "@type": "BreadcrumbList",
      "@id": "https://example.com/blog/json-ld-guide/#breadcrumb",
      "itemListElement": [
        { "@type": "ListItem", "position": 1, "name": "Blog", "item": "https://example.com/blog/" },
        { "@type": "ListItem", "position": 2, "name": "A practical guide to JSON-LD" }
      ]
    }
  ]
}

Wrap it in <script type="application/ld+json"> and </script>. Notice that the Person and Organization are described once and referenced twice. The layered view below shows how the six nodes connect.

How the six nodes in the example @graph reference each other Three layers. Site layer, present on every page: Organization #organization and WebSite #website, with WebSite publisher pointing to Organization. Page layer: WebPage #webpage, isPartOf WebSite, and BreadcrumbList #breadcrumb linked from the WebPage. Content layer: BlogPosting #article, whose mainEntityOfPage points to the WebPage, publisher points to the Organization, and author points to Person #person. Each arrow is an @id reference, not a copy. One @graph, six nodes, every arrow an @id reference Site-wide nodes are declared once; page nodes point up to them SITE PAGE CONTENT Organization WebSite WebPage Breadcrumb BlogPosting Person publisher isPartOf mainEntityOfPage publisher author

Scroll the diagram sideways to see all of it.

Source: Ambeltek diagram of the example above, using property names from schema.org.

Check the rendered page with both official tools: the Schema Markup Validator for schema.org validity and the Rich Results Test for Google eligibility. For the article and breadcrumb parts in detail, see Article schema and breadcrumb schema.

Why Google recommends JSON-LD over Microdata and RDFa

Google recommends it because it’s easiest to maintain, not because it’s worth more. The same introduction calls it “the easiest solution for website owners to implement and maintain at scale (in other words, less prone to user errors)”, and in the same section that “all 3 formats are equally fine for Google, as long as the markup is valid”.

JSON-LDMicrodataRDFa
Where it livesOne script blockAttributes on visible HTMLAttributes on visible HTML
Coupled to page layoutNoYesYes
Easy to express nested dataYesHarderHarder
Survives a redesignUsuallyOften breaksOften breaks
Can be injected by JavaScriptYes (Google reads it)Rarely practicalRarely practical
Risk of drifting from visible contentHigher; check itLower; it wraps the contentLower; it wraps the content

That last row is the honest trade-off. Microdata can’t drift from the page because it’s attached to the visible text. A script block is a separate copy, so a price or an opening time can change on the page while the script stays stale. Google’s guidelines say not to mark up content that isn’t visible, so generate the script from the same data as the page, not typed by hand.

Getting JSON-LD right across a site

The format works best as a system, not a set of snippets. A few decisions made once save a lot of cleanup later.

  1. Fix your @id scheme first. Decide the identifiers for your organization, website and each person, and document them. Changing them later splits your entities.
  2. Generate markup from data, not by hand. Pull names, prices, dates and hours from the same source the page uses, so the two can’t disagree.
  3. Render it on the server so every crawler sees it on the first request.
  4. Pick one source per entity. Themes, plugins and a schema markup generator can each emit an Organization. Keep one.
  5. Test by URL after every release with the validator and the Rich Results Test, and watch Search Console’s rich result reports for jumps in invalid items.

This markup is the plumbing for entity SEO: the clearer and more connected the graph, the less work any engine has to do to understand who you are. If your site has grown several overlapping blocks, or you want one graph designed from the start, our schema markup service builds and tests it against the rendered pages.

Frequently asked questions

What is JSON-LD used for?

JSON-LD is used to add structured data to web pages: machine-readable statements about what a page describes, such as an article, a product, a business or a person. Search engines read it to understand the page and to power rich results, and it is the format Google recommends for structured data.

What's the difference between JSON and JSON-LD?

JSON is a general data format with no agreed meaning for its keys. JSON-LD is JSON plus a small set of keywords, such as @context, @type and @id, that tie each key to a shared vocabulary and give things identifiers. Every JSON-LD document is valid JSON, but plain JSON is not linked data.

Where does JSON-LD go in HTML?

Inside a script tag with type application/ld+json, in either the head or the body of the page. Google's documentation says it reads JSON-LD in both places and can also read it when JavaScript injects it into the page, though server-rendered markup is more reliable for other crawlers.

Can a page have more than one JSON-LD script?

Yes. Google understands multiple items on a page whether you nest them or put each in its own block. If separate items describe related things, give them @id values and reference those, so engines know the pieces are connected.

What does @id mean in JSON-LD?

@id gives a thing a unique identifier, usually a URL with a fragment such as https://example.com/#organization. Any other node that references the same @id is talking about the same thing, which lets you describe an entity once and point to it from everywhere else.

Does JSON-LD help with rankings?

Not directly. Google describes structured data as a way to understand pages and enable rich results, and a structured data manual action removes rich result eligibility without affecting ranking. The benefit is clearer understanding and eligibility for enhanced results, not a ranking boost.

OK

Olga Kunger

Founder & Lead Strategist, Ambeltek

Olga leads Ambeltek's web development, AI SEO, and GEO work — helping brands rank on Google and get cited by AI engines. More about Olga →

Want structured data that engines trust?

We audit, design and ship connected JSON-LD for your whole site, validated and monitored.