Structured Data for Developers: JSON-LD, Schema.org, and Rich Results in Google

17 April, 2026 Web

Structured Data for Developers: JSON-LD, Schema.org, and Rich Results in Google

I added FAQ schema to a client's product page on a Friday afternoon. By Monday, the page had a rich snippet in Google — an expandable accordion showing the three most common questions right in the search results. Click-through rate jumped from 3.2% to 8.7% overnight. The page content did not change. The ranking did not change. The only difference was that Google now displayed the page with more visual real estate and more information than every competing result. That is the power of structured data — and most developers are leaving it on the table.


What Is Structured Data

Structured data is machine-readable code that describes what your page is about, not just what text it contains. A human reading your page can understand that it is a recipe with ingredients, or an article with an author, or a product with a price. A search engine crawler sees HTML — paragraphs, headings, and links — but cannot reliably infer semantic meaning. Structured data bridges that gap by explicitly declaring: "This page is an Article, written by Jane Smith, published on April 6th 2026."

The vocabulary comes from Schema.org, a collaborative standard maintained by Google, Microsoft, Yahoo, and Yandex. Schema.org defines hundreds of types — Article, Product, Recipe, Event, Person, Organization — each with specific properties.

The format for embedding this vocabulary into your page is JSON-LD (JavaScript Object Notation for Linked Data). It lives in a <script type="application/ld+json"> block in your HTML, completely separate from the visible content.


JSON-LD vs Microdata vs RDFa

Three formats exist for embedding structured data. JSON-LD won.

Format Syntax Where It Lives Google Preference
JSON-LD JSON in a <script> block Anywhere in <head> or <body> Recommended
Microdata HTML attributes (itemprop, itemscope) Inline within HTML elements Supported
RDFa HTML attributes (property, typeof) Inline within HTML elements Supported

JSON-LD is the clear winner for several reasons:

  • Decoupled from HTML. Your structured data lives in a separate script block. You can add, modify, or remove it without touching the page layout. Microdata and RDFa require weaving attributes into your existing HTML, which is fragile and hard to maintain.
  • Easy to generate server-side. JSON-LD is just a JSON object — any language can build it. PHP's json_encode, JavaScript's JSON.stringify, Python's json.dumps. No template gymnastics required.
  • Google explicitly recommends it. Google's documentation says: "Google recommends using JSON-LD for structured data whenever possible."
  • Easier to debug. A JSON-LD block is self-contained. You can validate it independently without parsing the surrounding HTML.

Google supports all three, but every example in Google's documentation uses JSON-LD, and their testing tools highlight JSON-LD blocks prominently. Use JSON-LD.


The Most Impactful Schema.org Types

Not all structured data types are equal. Some trigger visible rich results in Google search; others are consumed but produce no visible change. Here are the types that deliver the highest impact.

Article

The Article type (and its subtypes NewsArticle, BlogPosting) tells Google this page is editorial content with a specific author, publication date, and headline.

{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "Structured Data for Developers",
  "description": "A developer's guide to JSON-LD and Schema.org.",
  "image": "https://example.com/images/structured-data-guide.jpg",
  "author": {
    "@type": "Person",
    "name": "Jane Smith",
    "url": "https://example.com/authors/jane"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Example Dev",
    "logo": {
      "@type": "ImageObject",
      "url": "https://example.com/logo.png"
    }
  },
  "datePublished": "2026-04-06T13:00:00+00:00",
  "dateModified": "2026-04-06T13:00:00+00:00",
  "mainEntityOfPage": "https://example.com/blog/structured-data-guide"
}

Rich result: Enhanced article listing with headline, date, and author image. For news articles, this can also trigger inclusion in Google News and the Top Stories carousel.

Tip: dateModified matters. Google uses it as a freshness signal. Update it whenever you make meaningful content changes — do not set it to the current date on every page load.

FAQPage

The most visually impactful schema type for most sites. FAQ markup generates an expandable accordion directly in the search results, taking up significant visual space.

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is JSON-LD?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "JSON-LD (JavaScript Object Notation for Linked Data) is a format for embedding structured data in web pages using a JSON script block."
      }
    },
    {
      "@type": "Question",
      "name": "Does Google prefer JSON-LD over Microdata?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Google explicitly recommends JSON-LD and uses it in all their documentation examples."
      }
    }
  ]
}

Rich result: 2-3 expandable questions displayed below your search listing. This can double your visual footprint on the results page, pushing competitors below the fold.

Important: Google tightened FAQ rich result eligibility in 2023. FAQ rich results now only appear for well-known, authoritative government and health websites in general search. However, they still appear in other Google surfaces and are consumed by AI systems for answer generation. The structured data remains valuable even without the visible rich result.

HowTo

Step-by-step instructions get a dedicated visual treatment in search results, with numbered steps and optional images.

{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "How to Add Open Graph Meta Tags",
  "step": [
    {
      "@type": "HowToStep",
      "name": "Add the og:title tag",
      "text": "Add a meta tag with property='og:title' and your page title as the content."
    },
    {
      "@type": "HowToStep",
      "name": "Add the og:image tag",
      "text": "Add a meta tag with property='og:image' and the full URL to your preview image."
    }
  ]
}

Rich result: Numbered steps shown inline in search. Particularly effective for technical tutorials.

Product

For e-commerce, Product schema is the highest-impact type. It triggers rich results showing price, availability, review ratings, and shipping information directly in search.

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Developer Toolkit Pro",
  "description": "A comprehensive toolkit for web developers.",
  "image": "https://example.com/images/toolkit.jpg",
  "offers": {
    "@type": "Offer",
    "price": "49.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "reviewCount": "127"
  }
}

Rich result: Price, availability badge, and star rating shown directly in search results. The CTR difference between a plain listing and a product rich result can be 2-3x.

BreadcrumbList

Breadcrumb markup replaces the plain URL in search results with a structured path: Home > Blog > Structured Data Guide. This gives users a clear sense of your site hierarchy and makes the result more clickable.

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
    { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://example.com/blog" },
    { "@type": "ListItem", "position": 3, "name": "Structured Data Guide" }
  ]
}

The last item should omit the item property (URL) to indicate it is the current page.


Implementation Examples

PHP / Symfony

Build the JSON-LD as an associative array and encode it in Twig:

// In your controller
$jsonLd = [
    '@context'        => 'https://schema.org',
    '@type'           => 'Article',
    'headline'        => $article->title,
    'datePublished'   => $article->publishedAt->format('c'),
    'dateModified'    => $article->updatedAt?->format('c') ?? $article->publishedAt->format('c'),
    'author'          => ['@type' => 'Person', 'name' => 'Author Name'],
    'publisher'       => ['@type' => 'Organization', 'name' => 'Site Name'],
    'mainEntityOfPage' => $canonicalUrl,
];

return $this->render('article/show.html.twig', [
    'jsonLd' => json_encode($jsonLd, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
]);
{# In your Twig template #}
<script type="application/ld+json">{{ jsonLd|raw }}</script>

Next.js (App Router)

Next.js makes JSON-LD straightforward — just render a <script> tag in your component:

export default function ArticlePage({ post }) {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.publishedAt,
    dateModified: post.updatedAt,
    author: { '@type': 'Person', name: post.author },
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <article>{/* page content */}</article>
    </>
  );
}

WordPress

WordPress has several approaches. The simplest is using the wp_head action:

add_action('wp_head', function () {
    if (!is_single()) return;

    $post = get_post();
    $jsonLd = [
        '@context'      => 'https://schema.org',
        '@type'         => 'Article',
        'headline'      => get_the_title($post),
        'datePublished' => get_the_date('c', $post),
        'dateModified'  => get_the_modified_date('c', $post),
        'author'        => [
            '@type' => 'Person',
            'name'  => get_the_author_meta('display_name', $post->post_author),
        ],
    ];

    echo '<script type="application/ld+json">' . json_encode($jsonLd, JSON_UNESCAPED_SLASHES) . '</script>';
});

For production WordPress sites, plugins like Yoast SEO or Rank Math handle structured data automatically. But understanding the underlying JSON-LD helps you debug issues and add custom types.


Structured Data vs Open Graph vs Twitter Cards

These three systems are often confused because they all provide metadata about a page. They serve different consumers and are not interchangeable.

System Consumer Purpose Format
Open Graph Social media platforms (Facebook, LinkedIn, Discord, Slack, WhatsApp) Control how link previews look when shared <meta property="og:...">
Twitter Cards Twitter / X Control how link previews look on Twitter specifically <meta name="twitter:...">
Schema.org JSON-LD Search engines (Google, Bing), AI answer engines Trigger rich results, provide semantic understanding <script type="application/ld+json">

You need all three. Open Graph controls social sharing. Twitter Cards handle Twitter specifically (falling back to OG). Schema.org controls search appearance and feeds AI systems. There is some overlap in the data — all three include a title and description — but each serves a distinct system with its own rendering.

Generate your Open Graph tags and structured data independently. Validate that your OG and Twitter tags are correct with the Meta Tag Analyser, and test your structured data with Google's Rich Results Test.


Testing Structured Data

Google Rich Results Test

Google's Rich Results Test is the authoritative validation tool. Paste a URL or a code snippet and it shows which rich results your page is eligible for, any errors, and any warnings.

Check for:

  • All required properties are present
  • No warnings about recommended properties
  • The rich result preview matches your expectations

Schema Markup Validator

The Schema Markup Validator (formerly the Structured Data Testing Tool) validates against the full Schema.org specification, not just Google's supported subset. Use this to check correctness even for types Google does not render as rich results.

Chrome DevTools

In Chrome, open DevTools > Elements, then search for application/ld+json. This shows you exactly what structured data the page contains. Useful for debugging production pages.


Common Validation Errors

Error Cause Fix
Missing field "image" Article type requires an image Add the image property with a full URL
Missing field "author" Article requires an author Add author with @type: Person and name
Invalid date format Dates must be ISO 8601 Use 2026-04-06T13:00:00+00:00, not April 6, 2026
Page is not eligible for rich results The Schema type is valid but Google does not render it as a rich result Check Google's supported types list — not all Schema.org types trigger rich results
Missing field "name" in ListItem BreadcrumbList items need both name and item (except the last) Add the name property to each breadcrumb item
Incorrect @type Typo in the type name Schema.org types are case-sensitive: Article, not article

The most common mistake I see: forgetting that Schema.org types are case-sensitive. "@type": "article" is invalid. It must be "@type": "Article". JSON-LD parsers silently ignore types they do not recognise, so you get no error — just no rich result.


Automating Structured Data Validation in CI/CD

Just like meta tag validation, structured data checks should be automated to catch regressions before they reach production.

Using Google's API

Google provides a programmatic API for the Rich Results Test:

curl -X POST "https://searchconsole.googleapis.com/v1/urlTestingTools/mobileFriendlyTest:run" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-staging-site.com/page"}'

Simple Validation Script

For basic checks, extract the JSON-LD block and validate its structure:

#!/bin/bash
URL="$1"
JSON_LD=$(curl -sL "$URL" | grep -oP '(?<=<script type="application/ld\+json">).*?(?=</script>)')

if [ -z "$JSON_LD" ]; then
    echo "FAIL: No JSON-LD found on $URL"
    exit 1
fi

# Validate it is valid JSON
echo "$JSON_LD" | python3 -m json.tool > /dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "FAIL: Invalid JSON in JSON-LD block"
    exit 1
fi

echo "OK: Valid JSON-LD found"
echo "$JSON_LD" | python3 -m json.tool

Lighthouse CI

Lighthouse's SEO audit includes basic structured data validation. Combined with the meta tag checks described in the meta tag checklist, a Lighthouse CI step covers both traditional meta tags and structured data in a single pass.


Putting It All Together

A well-optimised page has three layers of metadata working together:

  1. HTML meta tags<title>, description, canonical, robots, viewport for baseline SEO
  2. Open Graph + Twitter Cards — social sharing previews across all platforms
  3. Schema.org JSON-LD — rich results in search and AI discoverability

Each layer serves a different consumer. Skipping any one of them means missing opportunities — broken social previews, plain search listings, or invisible pages to AI search engines. The implementation effort is small: a few meta tags and a JSON block. The payoff — richer search results, better social sharing, higher click-through rates — compounds on every page of your site.

Generate your Open Graph tags with the Open Graph Generator, validate everything with the Meta Tag Analyser, and preview the social sharing result with the Social Media Preview.

More Articles

CSV vs JSON for Data Exchange: When Each Format Wins

A practical comparison of CSV and JSON for APIs, data pipelines, and file exports. Covers structure, parsing, streaming, schema enforcement, size, tooling, and clear guidelines for choosing the right format.

15 April, 2026

SEO for AI Search: How to Optimise for ChatGPT, Perplexity, and Google AI Overviews

How AI-powered search engines discover, evaluate, and cite web content. Practical strategies for optimising your pages for ChatGPT Browse, Perplexity, Google AI Overviews, and other AI answer engines.

14 April, 2026

Image to Base64 Data URIs: When to Inline and When Not To

A practical guide to embedding images as Base64 data URIs. Covers the data URI format, size overhead, performance trade-offs, browser caching, Content Security Policy, and clear rules for when inlining helps vs hurts.

10 April, 2026