In short
- Structured data makes pages eligible for rich results. It guarantees nothing and isn't a ranking shortcut.
- For most sites the useful set is Organization and WebSite on the homepage, Article on posts, BreadcrumbList on deep pages and Product with Offer for shops.
- Google no longer shows HowTo (since 2023) or FAQ (since May 2026) rich results. Don't add them expecting a Google benefit.
- Use JSON-LD built from the same data you render, include every required property, mark up only visible content and escape < as \u003c.
- Validate with the Rich Results Test and the Schema Markup Validator. Google says AI Overviews need no special schema.
Structured data is a machine-readable description of a page, usually a JSON-LD <script type="application/ld+json"> using schema.org types, that tells search engines what the page is: an article, a product with a price, an app, an organization. Its main practical payoff is eligibility for rich results in Google. It is not a ranking shortcut, and Google no longer shows several rich results developers still add markup for, including FAQ and HowTo. For most SaaS, docs and blog sites, the useful set is small: Organization and WebSite on the homepage, Article on posts, BreadcrumbList on deep pages, and Product with an Offer if you sell physical goods. Build it from the same data you render, include every required property, and validate it.
What structured data does (and doesn't)
What it does:
- Makes pages eligible for rich results: product prices and availability, review stars, article images and dates, breadcrumbs, software app details.
- Removes guesswork: your site name, logo, publish dates and prices are stated explicitly instead of inferred from the layout.
- Feeds Search Console reports: each supported type gets an enhancement report with errors and warnings per URL.
What it doesn't do:
- It doesn't guarantee anything. Google's policies say it does not guarantee that your structured data will show up in results, even when it passes the Rich Results Test.
- It doesn't fix indexing. Markup on a page that isn't indexed, or isn't in the server HTML, does nothing. Fix crawling, canonicals and content first (the Next.js SEO checklist covers the order).
- It isn't a stated ranking factor. Google's documentation describes it as a way to enable rich results, not a way to rank higher.
Which types are worth adding
Add types that describe what the page really is and that Google (or another consumer you care about) actually uses. Everything else is weight in your HTML.
| Type | Where | Google feature today | Required for that feature |
|---|---|---|---|
| Organization | Homepage | Logo and details in knowledge panels | None; name, url, logo recommended |
| WebSite | Homepage only | Site name shown in results | name, url |
| Article / BlogPosting | Posts, guides, docs articles | Article appearance (title, image, date) | None; headline, image, datePublished, author recommended |
| BreadcrumbList | Any page below the top level | Breadcrumb trail (desktop only since January 2025) | itemListElement with position, name, item |
| Product + Offer | Product pages (e-commerce) | Product snippets and merchant listings | Merchant listing: name, image, offers with price and priceCurrency |
| SoftwareApplication | App or SaaS product page | Software app rich result | name, offers.price, and aggregateRating or review |
| FAQPage | Pages with visible Q&A | None: FAQ rich results stopped showing in May 2026 | n/a |
| HowTo | Step-by-step pages | None: removed from results in 2023 | n/a |
SaaS
Organization plus WebSite on the homepage is the baseline. SoftwareApplication is tempting, but Google's software app feature requires a rating or review. If you have no real, on-page reviews, skip it rather than inventing an aggregateRating.
Docs and blogs
Article (or BlogPosting, or TechArticle) on each post, with headline, image, datePublished, dateModified and an author with a name and url. Add BreadcrumbList on docs pages that sit several levels deep.
E-commerce
Product with a nested Offer is where structured data pays off most. Merchant listings need name, image and offers with a price and an ISO 4217 priceCurrency, and Google requires a price greater than zero for them.
What Google stopped showing
Google has been trimming the rich result gallery for years. If an older tutorial tells you to add one of these, check the current search gallery first.
| Feature | What changed | When |
|---|---|---|
| HowTo | No longer shown on desktop or mobile; docs removed | September 2023 |
| FAQ | Restricted to well-known government and health sites | September 2023 |
| FAQ | No longer shown in Google Search at all; docs removed | May to June 2026 |
| Sitelinks search box | No longer shown; docs removed | November 2024 |
| Breadcrumbs | Shown on desktop only | January 2025 |
| Course info, estimated salary, learning video, special announcement, vehicle listing | No longer shown; docs removed | September 2025 |
| Practice problem | No longer shown; docs removed | January 2026 |
Removing these is optional: Google's changelog only says the features are no longer shown. But it is code you maintain for no Google benefit, and if other consumers matter to you (Bing, AI crawlers), make that a deliberate choice rather than a leftover.
JSON-LD vs microdata
Google supports three formats: JSON-LD, microdata and RDFa. It says all three are equally fine when valid, and recommends JSON-LD. For developers JSON-LD is the obvious choice: it is one object built from your data, kept separate from your markup, so a design change can't break it.
<div itemscope itemtype="https://schema.org/Product">
<h1 itemprop="name">Trail Runner 2</h1>
<div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
<span itemprop="priceCurrency" content="USD">$</span><span itemprop="price" content="129.00">129</span>
</div>
</div>Required vs recommended properties
Each Google feature lists required and recommended properties. Google's rule: you must include all the required properties for an object to be eligible. Recommended properties improve the result, and Google says fewer, complete and accurate recommended properties beat filling every field with weak data.
Two rules decide most validation results:
- Requirements are per feature, not per schema.org type. schema.org lets
Producthave almost anything. Google's merchant listing needsimageandoffers; a product snippet acceptsoffers,revieworaggregateRating. - Only mark up visible content. Google's policies say: don't mark up content that is not visible to readers of the page. The price in your JSON-LD must be the price on the page. Breaking this can lead to a manual action that removes the page's rich result eligibility.
Structured data in Next.js
Next.js recommends rendering JSON-LD as a <script> in layout.tsx or page.tsx. JSON.stringify doesn't escape HTML, so a product name containing </script> could break out of the tag. The Next.js docs suggest replacing < with \u003c. Wrap that in one component:
// Server Component: the markup is in the HTML Google receives.
export function JsonLd({ data }: { data: Record<string, unknown> }) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(data).replace(/</g, "\\u003c"),
}}
/>
);
}Homepage: Organization and WebSite
import { JsonLd } from "@/components/json-ld";
const site = "https://your-site.com";
export default function Home() {
return (
<main>
<JsonLd
data={{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": `${site}/#org`,
name: "Acme",
url: site,
logo: `${site}/logo.png`,
sameAs: ["https://github.com/acme", "https://x.com/acme"],
},
{
"@type": "WebSite",
"@id": `${site}/#website`,
name: "Acme",
url: site,
publisher: { "@id": `${site}/#org` },
},
],
}}
/>
{/* ... */}
</main>
);
}Posts: Article and BreadcrumbList from the same data
import { notFound } from "next/navigation";
import { JsonLd } from "@/components/json-ld";
const site = "https://your-site.com";
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound();
const url = `${site}/blog/${post.slug}`;
return (
<article>
<JsonLd
data={{
"@context": "https://schema.org",
"@graph": [
{
"@type": "BlogPosting",
headline: post.title,
image: [`${site}${post.cover}`],
datePublished: post.publishedAt, // ISO 8601
dateModified: post.updatedAt,
author: { "@type": "Person", name: post.author.name, url: post.author.url },
mainEntityOfPage: url,
},
{
"@type": "BreadcrumbList",
itemListElement: [
{ "@type": "ListItem", position: 1, name: "Blog", item: `${site}/blog` },
{ "@type": "ListItem", position: 2, name: post.title, item: url },
],
},
],
}}
/>
<h1>{post.title}</h1>
{/* the same title, date and author rendered visibly */}
</article>
);
}A framework-agnostic snippet
Astro, SvelteKit, Remix, a static generator: the pattern is the same. Build an object, serialize it with < escaped, and print it into the server HTML. Here is a product page with an Offer:
export function jsonLdScript(data: object): string {
const json = JSON.stringify(data).replace(/</g, "\\u003c");
return `<script type="application/ld+json">${json}</script>`;
}
export function productJsonLd(p: {
name: string; image: string; description: string; sku: string;
price: number; currency: string; inStock: boolean; url: string;
}) {
return {
"@context": "https://schema.org",
"@type": "Product",
name: p.name,
image: [p.image],
description: p.description,
sku: p.sku,
offers: {
"@type": "Offer",
url: p.url,
price: p.price.toFixed(2),
priceCurrency: p.currency, // ISO 4217, e.g. "USD"
availability: p.inStock ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
},
};
}In Astro, render it with <script type="application/ld+json" set:html={json} />; in SvelteKit, inside <svelte:head> with {@html jsonLdScript(data)}. In plain HTML, paste the script anywhere in the <head> or <body>.
Validate and monitor it
- 1BuildFrom the same data the page renders
- 2Schema Markup ValidatorIs it valid schema.org?
- 3Rich Results TestIs it eligible for a Google feature?
- 4Search ConsoleEnhancement reports after Google recrawls
- Check the server HTML
Markup that only appears after client JavaScript runs is easy to miss, and one syntax error hides a whole block. This prints the types in each JSON-LD block the server sends, or the parse error:
Terminal curl -s https://your-site.com/blog/launch | node -e ' let h = ""; process.stdin.on("data", (d) => (h += d)).on("end", () => { for (const [, j] of h.matchAll(/<script type="application\/ld\+json"[^>]*>(.*?)<\/script>/gs)) { try { const d = JSON.parse(j); console.log("ok ", [].concat(d["@graph"] ?? d).map((x) => x["@type"]).join(", ")); } catch (e) { console.log("FAIL", e.message); } } });' # ok BlogPosting, BreadcrumbList - Run the Rich Results Test
The Rich Results Test shows which Google features the page is eligible for, and which required or recommended properties are missing. It only knows types Google supports.
- Run the Schema Markup Validator
The Schema Markup Validator checks any schema.org type, including ones Google ignores. Use it for syntax and nesting problems.
- Watch Search Console
After deployment, the rich result reports (Products, Breadcrumbs and so on) list errors and warnings per URL. Syntax errors don't show there, because Google couldn't tell which feature the block was for: they go to the Unparsable structured data report, which only appears if Google found some. Use URL Inspection to confirm Google found the markup on a specific page, and Validate fix once every instance is fixed.
Troubleshooting: symptom, cause, fix
| Symptom | Likely cause | Fix |
|---|---|---|
| Rich Results Test passes, no rich result in search | Eligibility isn't a promise, or the page isn't indexed yet | Check indexing in URL Inspection. Allow several days for a recrawl, and check the Manual Actions report |
| Entry in Unparsable structured data | Invalid JSON: trailing comma, stray backslash, a duplicate @context | Build the object in code and serialize it with JSON.stringify. Never hand-assemble JSON strings |
| Error: missing field (a required property) | Template has no data for it on some pages | Fill it from real page data, or don't emit that type on pages that lack it |
| Warning: missing recommended field | Optional property absent | Add it if you have accurate data. Warnings don't block eligibility |
| Markup present locally, absent in URL Inspection | Rendered only on the client, or on a code path production doesn't take | Render it in the server HTML and check with the script above |
| Article image not used | Image blocked by robots.txt, relative URL or too small | Absolute, crawlable image URLs. Google recommends at least 50K pixels (width × height) in 16x9, 4x3 and 1x1 |
| Dates off by hours | datePublished without a timezone | Use full ISO 8601 with an offset. Without one, Google assumes Googlebot's timezone |
| Two Organization objects with different names | Layout and page both emit one | One source per type per page. Link related items on a page with @id |
| Rich results dropped after a deploy | A template change removed a required property | Check the rich result reports after each release, or fail the build on missing fields |
Brief your coding agent
Agents add schema eagerly and from memory, which is how sites end up with HowTo blocks and invented ratings. Give the rules up front:
Add JSON-LD structured data to this site.
- Use only these types: Organization + WebSite (homepage), BlogPosting (posts),
BreadcrumbList (pages two or more levels deep). Nothing else without asking.
- Build each object from the same data the page renders. Every value must be
visible on the page. Never invent ratings, reviews or prices.
- One JsonLd server component; JSON.stringify with < escaped as \u003c.
- Include every property Google lists as required for that feature; skip a
recommended one rather than filling it with weak data.
- Dates: full ISO 8601 with timezone. Images: absolute URLs.
- Show me the curl output of the parse check for one page of each type.Structured data and AI answers
It is easy to find claims that schema markup gets you cited in AI answers. Be careful with them. Google's guidance on AI Overviews and AI Mode says there is no special schema.org structured data you need to add, and no additional requirements beyond normal Search eligibility. Its one structured data note is that markup should match the visible text on the page.
A reasonable position: structured data states facts (prices, dates, authors, your organization's name) unambiguously, which can only help any system that reads it. It does not replace clear text in the HTML, which is what AI systems quote. Our AI Overviews guide covers what does help.
Checklist
- JSON-LD is rendered on the server and has
<escaped as\u003c. OrganizationandWebSiteon the homepage only;Articleon posts;BreadcrumbListon deep pages.- Every required property for the targeted Google feature is present, from real page data.
- Every marked-up value is visible on the page (price, rating, author, date).
- No
HowTomarkup kept for Google, and no FAQ markup added expecting FAQ rich results. - The Rich Results Test and the Schema Markup Validator both pass.
Serpkin checks structured data on every scan against a built-in rule table for Google's common types. It flags markup missing required properties (schema_invalid), valid markup missing recommended properties (schema_incomplete), pages that look like articles or products but carry no matching markup (schema_missing_eligible), and leftover HowTo markup (schema_deprecated). Missions come with a JSON-LD snippet listing the required properties for your agent to fill in, and npx serpkin check fails a preview whose JSON-LD is invalid JSON or newly misses required properties. To test one URL now, use the free SEO checker.
Questions
Is structured data a ranking factor?
Google doesn't describe it as one. Its documentation presents structured data as a way to become eligible for rich results, which can make a listing more useful and clickable. It doesn't guarantee those results appear.
Does Google still show FAQ rich results?
No. Google restricted FAQ rich results to well-known government and health sites in 2023, and its changelog says the feature stopped appearing in Google Search from May 7, 2026. HowTo rich results were removed in 2023.
Is JSON-LD better than microdata?
Google supports JSON-LD, microdata and RDFa equally when they are valid, and recommends JSON-LD. For developers it is easier to maintain because it is one object built from your data, separate from the HTML.
Can I add JSON-LD with JavaScript?
Google says it can read JSON-LD injected by JavaScript, but rendering it in the server HTML is safer: it is seen on the first crawl and by tools and crawlers that don't run JavaScript.
Do I need schema markup to appear in AI Overviews?
No. Google says there is no special schema.org structured data needed for AI Overviews or AI Mode. Clear, crawlable text on an indexable page matters more. Structured data should simply match what the page shows.
Sources
- Google: Introduction to structured data (checked October 27, 2026)
- Google: Structured data general guidelines (checked October 27, 2026)
- Google: Search gallery of structured data features (checked October 27, 2026)
- Google Search Central: documentation updates (HowTo, FAQ, sitelinks search box, breadcrumbs) (checked October 27, 2026)
- Google: Merchant listing structured data (checked October 27, 2026)
- Google: Software app structured data (checked October 27, 2026)
- Google: Article structured data (checked October 27, 2026)
- Google: Site names (WebSite markup) (checked October 27, 2026)
- Search Console Help: Unparsable structured data report (checked October 27, 2026)
- Google: AI features and your website (checked October 27, 2026)
- Next.js docs: JSON-LD (checked October 27, 2026)