JSON-LD is the structured data format recommended by Google and the one most AI systems understand natively. It lives in a <script> tag in your page head, separate from your HTML content, and describes the page in terms that machines can process without parsing your prose.

The format itself is simple. The implementation errors are subtle and common. Here is how to avoid them.

The basic structure

Every JSON-LD block needs two things at minimum: a @context of https://schema.org and a @type that identifies what kind of thing the page describes. Everything else depends on the type.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "The title of the article",
  "author": {
    "@type": "Person",
    "name": "Krisada"
  },
  "datePublished": "2026-04-14",
  "description": "A short description of the article."
}
</script>

The most common error at this level: putting the @context URL inside the @type value, or using http:// instead of https://. Both cause the block to fail validation silently.

Article markup

For an article page, the minimum viable block includes headline, author (as a Person object with a name), datePublished, and description. Add dateModified when you update content - it signals freshness to both search engines and AI systems.

The author field should always be an object, not a string. "author": "Krisada" is technically valid but less useful than "author": {"@type": "Person", "name": "Krisada"}. The object form lets you add url and sameAs properties later that strengthen the author entity.

Breadcrumb markup

BreadcrumbList is one of the most useful types for a content library site. It tells machines the navigational hierarchy of the current page - which category it belongs to, what the parent category is.

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Library",
      "item": "https://www.krisada.com/library/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Content Systems",
      "item": "https://www.krisada.com/library/content-systems/"
    }
  ]
}

The most common error: position values starting at 0 instead of 1. Schema.org specifies that positions in a BreadcrumbList start at 1. Validators will flag 0-indexed lists.

Multiple blocks on one page

A page can have multiple JSON-LD blocks - one for the article, one for the breadcrumb, one for the author. They do not need to be combined into a single block. Each <script type="application/ld+json"> tag is read independently.

Keep them separate. Combined blocks with multiple types in a single object are harder to read, harder to maintain, and more likely to introduce validation errors from type conflicts.

Testing your implementation

Google's Rich Results Test and Schema.org's validator will catch most structural errors. Run every new template through one of them before deploying. A block that passes validation in both tools is almost certainly being read correctly by search engines and AI retrieval systems.

If you generate markup programmatically from JSON content records - as a flat-file PHP site does - test the template once with a representative record. The same template logic will produce valid markup for every record that shares that schema, so you only need to validate the template, not every page it generates.