How to Add Schema Markup to HTML with JSON-LD
This tutorial is part of the SEO series for web developers. You should be comfortable with HTML basics and JavaScript objects.
Search engines are good at reading text, but they still have to guess what a page is. Is "The CSS Box Model" an article, a product or a course? Who wrote it, and when? Structured data lets you answer those questions directly, in a format machines don't have to guess at. Get it right and your pages become eligible for rich results such as breadcrumbs, FAQ snippets, article dates and product ratings.
What JSON-LD looks like
There are a few ways to add structured data, but JSON-LD is the one Google recommends and the easiest to maintain. It is a plain JSON object inside a script element. It doesn't change how the page looks, and it can go in the head or the body.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "The CSS Box Model",
"datePublished": "2021-03-07",
"author": { "@type": "Organization", "name": "REM Web Development" },
"mainEntityOfPage": "https://www.remwebdevelopment.com/blog/css/css-box-model.html"
}
</script>
- @context is always https://schema.org. It says which vocabulary the property names come from.
- @type is what the thing is. Schema.org defines hundreds of types. The common ones are Article/BlogPosting, Product, LocalBusiness, Course, FAQPage and BreadcrumbList.
- Everything else is properties of that type. A property's value can be another object with its own @type, like the author above.
A useful second example: breadcrumbs
Breadcrumb markup is one of the easiest rich results to earn, because most sites already show breadcrumbs. It replaces the plain URL in search results with a readable trail like Home › Blog › CSS:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.remwebdevelopment.com/" },
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.remwebdevelopment.com/blog/" },
{ "@type": "ListItem", "position": 3, "name": "CSS", "item": "https://www.remwebdevelopment.com/blog/css/" }
]
}
</script>
A page can have several JSON-LD blocks, so an article page can have one for the article and one for the breadcrumbs.
Generate it, don't hand-write it
Hand-written JSON is fragile. One missing comma and the whole block is ignored, with no visible error on the page. Build the object in JavaScript and let JSON.stringify() handle the commas and quotes:
function articleSchema(post) {
return {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.summary,
datePublished: post.date, // ISO format: 2026-10-13
author: { '@type': 'Organization', name: 'REM Web Development' },
mainEntityOfPage: post.url,
};
}
const script = document.createElement('script');
script.type = 'application/ld+json';
script.textContent = JSON.stringify(articleSchema(post));
document.head.append(script);
Adding it in the browser like this works, but it is better to output it in the HTML on the server or at build time, so crawlers see it without running JavaScript. In Astro that is one line in the layout: <script type="application/ld+json" set:html={JSON.stringify(schema)} />. Every article on this site gets its BlogPosting and BreadcrumbList markup that way.
Rules Google actually enforces
- Mark up what is visible. The structured data has to describe content the visitor can see. Marking up reviews or FAQs that aren't on the page counts as spam and can lead to a manual action.
- Use the required properties for the rich result you're after. Each type in Google's documentation lists "required" and "recommended" fields.
- Dates in ISO 8601 (2026-10-13 or 2026-10-13T09:00:00+07:00), absolute URLs, real values. No placeholders.
- Valid markup is not guaranteed rich results. It makes a page eligible. Google decides whether to show them.
Testing
- Paste the URL or the code into Google's Rich Results Test to see which rich results the page is eligible for and any errors.
- Use the Schema Markup Validator (validator.schema.org) to check the markup against the full schema.org vocabulary, beyond what Google uses.
- After launch, Search Console shows an "Enhancements" report for each structured data type it finds across your site.
That is enough to add correct structured data to a blog or a small business site. For a longer guide covering more types (LocalBusiness, Product, FAQ, HowTo) and which rich results each one can earn, see Southern Whale's Schema Markup Guide.