jsonjson-ldseoschema-org

JSON-LD & Schema.org: Structured Data for SEO Explained

·9 min read·JSON Concepts

The JSON search engines actually read

Every rich result you've seen in Google — star ratings under a recipe, an FAQ accordion right in the search results, a breadcrumb trail instead of a raw URL — comes from a block of JSON quietly embedded in the page's HTML. That JSON is JSON-LD (JSON for Linking Data), and the vocabulary it uses is almost always schema.org. This isn't a proprietary Google format — it's a W3C-backed standard, and once you can read it, you can read the SEO markup on any site's page source.

What it looks like on a real page

JSON-LD sits inside a <script> tag with a special type, invisible to visitors but fully readable by crawlers:

html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Recipe",
  "name": "Classic Margherita Pizza",
  "author": { "@type": "Person", "name": "Ada Lovelace" },
  "prepTime": "PT20M",
  "cookTime": "PT15M",
  "recipeYield": "4 servings",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "reviewCount": "412"
  }
}
</script>

Two fields anchor every JSON-LD block: `@context` tells parsers which vocabulary you're using (almost always https://schema.org), and `@type` names the schema.org type this object represents — Recipe, Product, Article, FAQPage, and hundreds more are defined at schema.org.

The types that actually drive rich results

Google supports dozens of schema.org types for rich results, but a handful cover most sites:

TypeWhat it powersKey fields
OrganizationKnowledge panel, logo in searchname, url, logo
BreadcrumbListBreadcrumb trail instead of a raw URLitemListElement (ordered ListItems)
FAQPageExpandable FAQ snippets in resultsmainEntity (array of Question/Answer)
ProductPrice, availability, star rating in resultsoffers, aggregateRating
Article / BlogPostingHeadline, author, date in resultsheadline, author, datePublished
HowToNumbered step-by-step rich resultstep (ordered HowToSteps)
SoftwareApplicationApp info cardapplicationCategory, offers

A minimal FAQPage — the exact shape behind FAQ accordions ranking with extra visible space in search results:

json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Is JSON-LD visible to site visitors?",
      "acceptedAnswer": { "@type": "Answer", "text": "No — it lives in a script tag and is never rendered." }
    }
  ]
}

Linking objects together with @id

Real sites don't emit one isolated blob per page — they reference a shared Organization or WebSite object across every page using an @id, so a crawler can tell that the "JSONKit" mentioned on ten different pages is the same entity:

json
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://example.com/#organization",
  "name": "Example Inc",
  "url": "https://example.com"
}

Any other JSON-LD block on the site can then reference "publisher": { "@id": "https://example.com/#organization" } instead of repeating the whole object — the same normalization idea as a foreign key in a database, expressed in JSON.

Generating it in code

In a React/Next.js app, JSON-LD is just a script tag with stringified JSON — no special library required:

jsx
function ArticleJsonLd({ title, author, datePublished }) {
  const data = {
    "@context": "https://schema.org",
    "@type": "Article",
    headline: title,
    author: { "@type": "Person", name: author },
    datePublished,
  };
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
    />
  );
}

The one real gotcha: escape untrusted content. If title or author ever comes from user input, a value like </script><script>alert(1)</script> breaks out of the block — JSON.stringify alone doesn't escape </. Replace < with \u003c before injecting, or use a templating layer that does it for you.

Validating your structured data

Two checks matter before you ship JSON-LD: is it valid JSON, and does it satisfy the schema.org type's expected shape? Malformed JSON-LD is silently ignored by Google rather than erroring, so it's easy to ship broken markup and never notice.

  1. Paste the block into JSONKit's JSON Validator to catch syntax errors first.
  2. Run the live page through Google's Rich Results Test to confirm Google actually recognizes the type and fields.
  3. For a reusable content shape, sketch it as a JSON Schema so every page on your site emits a consistent structure.

Frequently asked questions

Meta tags (title, description) affect how a snippet looks; JSON-LD is what unlocks *rich results* — star ratings, FAQ accordions, breadcrumbs — which meta tags cannot do. They're complementary, not alternatives.

Yes — it's common to have separate <script type="application/ld+json"> blocks for Organization, BreadcrumbList, and the page's main content type (Article, Product, etc.) all on the same page.

It doesn't get penalized, but it is simply ignored — you lose the rich-result opportunity without any error surfacing, which is why validating the JSON syntax and testing with Google's tool both matter.

No — Microdata and RDFa embed the same schema.org vocabulary directly in HTML attributes instead of a separate script block. Google explicitly recommends JSON-LD because it's easier to generate, validate, and keep separate from your markup.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.