Skip to content
toolsdocks

Structured data and JSON-LD basics for small websites

8 min read · Updated 5 October 2026

A human can look at a page and see that "£24.99" is a price, that "Tue 14 Oct, 7:30 pm" is when a concert starts, and that the address at the bottom is where a shop is. Software has to guess. Structured data removes the guessing: it is a small, machine-readable description of what a page is about, added to the page's code.

Search engines use structured data to understand pages and, for some types, to show richer results: product prices and availability, event dates, recipe cooking times, breadcrumb trails instead of a bare URL. Other software, such as assistants, link previews and shopping tools, can read it too.

This guide covers the essentials: the vocabulary, the format, the handful of types most small sites need, and the mistakes that cause markup to be ignored.

Schema.org and JSON-LD

Two separate things are involved:

  • Schema.org is a shared vocabulary: a list of types (Organization, Product, Event, Article, Recipe…) and the properties each can have (name, price, startDate, author…). It is maintained jointly by the major search engines and used across the web.
  • JSON-LD is the format most commonly used to write that vocabulary into a page. It is a block of JSON inside a script tag, usually in the page's <head>, and it does not change how the page looks.

A minimal example:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Elmwood Dental",
  "url": "https://www.elmwooddental.example",
  "logo": "https://www.elmwooddental.example/logo.png"
}
</script>
  • @context says which vocabulary is being used (always https://schema.org here).
  • @type says what kind of thing is described.
  • The remaining keys are properties of that type.

Older formats (Microdata and RDFa) add attributes directly to the visible HTML. They still work, but JSON-LD is easier to write, read and maintain because it sits in one block separate from the layout. Search engines generally recommend it.

What structured data can and cannot do

It helps to be realistic:

  • It makes a page eligible for certain enhanced results. It does not guarantee them. Search engines decide whether to show them based on the query, the device, the quality of the page and other factors.
  • It is not a direct ranking boost. A page does not move up simply because it has markup. The benefit is a clearer understanding of the page and, sometimes, a more informative listing.
  • It must describe what is visible on the page. Marking up reviews, prices or FAQs that visitors cannot see is against search engine guidelines and can lead to manual action.
  • Supported features change. For example, Google stopped showing How-to rich results in 2023, removed the sitelinks search box in 2024, and has since retired FAQ rich results as well (its Search Central documentation updates page records each change). The markup is still valid schema.org, but it no longer produces those displays in Google. Check current documentation before investing time in a particular result type.

The types most small sites need

Site or page Useful types
Any business or organization (home or about page) Organization, or LocalBusiness and a more specific subtype
Blog posts and news Article (or BlogPosting / NewsArticle)
Product pages Product with Offer
Events Event
Recipes Recipe
Pages deep in a site structure BreadcrumbList
Job adverts JobPosting

You do not need all of them. Pick the ones that match your real content, and do them completely and accurately.

Example: a local business

For a business with a physical location, LocalBusiness (or a more specific subtype such as Dentist, Restaurant or HardwareStore) can include the address, phone number, opening hours and location:

{
  "@context": "https://schema.org",
  "@type": "Dentist",
  "name": "Elmwood Dental",
  "url": "https://www.elmwooddental.example",
  "telephone": "+44 113 496 0000",
  "image": "https://www.elmwooddental.example/images/practice.jpg",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "1 Elmwood Lane",
    "addressLocality": "Leeds",
    "postalCode": "LS1 0AA",
    "addressCountry": "GB"
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      "opens": "08:30",
      "closes": "17:30"
    }
  ]
}

Notice that address is not a plain string; it is a nested object with its own type. Nesting like this is how schema.org connects things: an Event has a location that is a Place, a Product has offers that are Offers, an Article has an author that is a Person or Organization.

Telephone numbers are best written in international format, and addressCountry uses a two-letter country code. Opening hours use 24-hour times.

Example: a product

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Slim Bifold Wallet – Chestnut",
  "image": "https://www.hartley.example/img/bifold-chestnut.jpg",
  "description": "Hand-stitched bifold wallet in vegetable-tanned leather, six card slots.",
  "sku": "HB-BF-CH",
  "brand": { "@type": "Brand", "name": "Hartley & Co" },
  "offers": {
    "@type": "Offer",
    "price": "45.00",
    "priceCurrency": "GBP",
    "availability": "https://schema.org/InStock",
    "url": "https://www.hartley.example/wallets/slim-bifold-chestnut"
  }
}

Three details here cause most product errors:

  • price is a number without a currency symbol ("45.00", not "£45"), and the currency goes in priceCurrency as a three-letter code (GBP, USD, EUR).
  • availability uses a full schema.org value, such as https://schema.org/InStock or https://schema.org/OutOfStock, not free text.
  • The price must match the page. If the page shows a sale price, the markup should too. Mismatches are a common reason for product results being dropped.

Ratings (aggregateRating) and reviews can be added when they are genuine reviews shown on the page. Search engines generally do not display review stars for reviews a business publishes about itself on its own Organization or LocalBusiness markup.

Example: an article with breadcrumbs

A page can contain more than one item. A blog post often has an Article and a BreadcrumbList describing where it sits in the site:

[
  {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "headline": "How to Care for Vegetable-Tanned Leather",
    "image": "https://www.hartley.example/blog/leather-care.jpg",
    "datePublished": "2026-09-12T09:00:00+01:00",
    "dateModified": "2026-10-01T16:20:00+01:00",
    "author": { "@type": "Person", "name": "Sam Okafor" }
  },
  {
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
      { "@type": "ListItem", "position": 1, "name": "Blog", "item": "https://www.hartley.example/blog" },
      { "@type": "ListItem", "position": 2, "name": "Leather care", "item": "https://www.hartley.example/blog/leather-care" }
    ]
  }
]

Dates use the ISO 8601 format: 2026-09-12 for a date, or a date and time with a time-zone offset as shown. Without an offset, a time is ambiguous, which matters most for events.

Connecting items with @id

On larger sites, the same organization appears as the publisher of every article and the seller of every product. Instead of repeating it everywhere, you can give it an identifier with @id and refer to it:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://www.hartley.example/#org",
      "name": "Hartley & Co",
      "url": "https://www.hartley.example/"
    },
    {
      "@type": "WebSite",
      "@id": "https://www.hartley.example/#website",
      "url": "https://www.hartley.example/",
      "name": "Hartley & Co",
      "publisher": { "@id": "https://www.hartley.example/#org" }
    }
  ]
}

The @id is just a unique name, conventionally your URL with a fragment such as #org. It does not need to be a page that exists. @graph lets you put several connected items in one block. Small sites can skip this entirely; it is a tidiness feature, not a requirement.

Adding structured data step by step

  1. Choose the type that matches the page's main content. One page, one main subject.
  2. Collect the facts from the visible page: name, price, dates, address, author. Do not invent values.
  3. Generate the JSON-LD. The structured data generator builds valid JSON-LD for Article, Product, Organization, LocalBusiness, Event, FAQPage, BreadcrumbList, Recipe and other types from a form, so you do not have to remember property names or nesting.
  4. Validate the JSON itself. A single missing comma makes the whole block unreadable. Paste it into the JSON formatter, which pinpoints syntax errors by line and column.
  5. Add it to the page, inside a <script type="application/ld+json"> element. Most CMSs and site builders have a field or plugin for this; in a template, generate it from the same data that fills the page so the two cannot drift apart.
  6. Check the live page. Open the published page's HTML in the SEO checker to confirm the structured data is actually present, then use the search engines' own rich result testing tools to see which features the page is eligible for.
  7. Monitor. Search engine webmaster consoles report structured data errors and warnings across your site, which is how template problems usually get spotted.

Common mistakes

  • Invalid JSON. Trailing commas after the last item, missing commas between properties, and unescaped quotation marks inside text are the most common. JSON does not allow comments either.
  • Curly "smart" quotes. Text pasted from a word processor often replaces " with “ and ”, which JSON does not accept.
  • Relative URLs. Use full URLs such as https://www.hartley.example/img/a.jpg, not /img/a.jpg.
  • Wrong value formats. Prices with currency symbols, dates like "12/09/2026" (which is ambiguous between countries), durations written as "1 hour" instead of the ISO format PT1H used by Recipe and similar types.
  • Markup that does not match the page. A price, rating or date in the markup that differs from the visible text, or FAQ markup for questions that are not on the page.
  • The same markup on every page. Putting product markup in a site-wide template so it appears on the contact page, or giving every page the same Article headline.
  • Conflicting duplicates. A theme and a plugin each adding their own Organization block with different details. Pick one source.
  • Expecting immediate results. Search engines need to recrawl the page, and many features appear only for some queries.

FAQ

Do I need structured data for my site to appear in search? No. Search engines index pages without it. Structured data adds clarity and makes some enhanced displays possible.

Where should the JSON-LD go? Usually in the <head>, but anywhere in the HTML works. What matters is that it is in the HTML the search engine receives. Markup added by JavaScript after the page loads is often read too, but static markup is more dependable.

Which properties are required? Each search feature has its own required and recommended properties, and they differ from the full schema.org definitions. Fill in every property you have accurate information for; the required ones are the minimum for eligibility.

Can I use more than one type on a page? Yes, as long as each describes something genuinely on that page, such as an article plus its breadcrumbs. Avoid marking up unrelated items just to add more types.

Is FAQPage markup still worth adding? It remains valid schema.org and other software may read it, but Google no longer shows FAQ rich results, so do not add it expecting a special search display. Visible questions and answers can still help visitors; mark them up only if they are on the page.