BreadcrumbList schema: working implementation patterns and the mistakes that break them
BreadcrumbList schema replaces the URL line in Google's mobile SERP with your breadcrumb trail — a small but visible win when implemented correctly. Here's the working JSON pattern, the two acceptable formats and when to use each, platform-specific examples for WordPress, Next.js, Webflow, and headless setups, plus the position numbering errors that quietly break the implementation.
BreadcrumbList schema gets less attention than FAQPage or Article markup, but it has a specific visible benefit — it replaces the URL line in Google's mobile SERP with your breadcrumb trail, improving both UX and how prominently your result displays. The implementation is simple in concept and trips up surprisingly often in practice: position numbering errors, wrong format choices, and inconsistency with the visible breadcrumb on the page. This piece covers the working JSON pattern, the two acceptable formats and when each makes sense, platform-specific implementations for the systems most content sites use, and the common mistakes that quietly break the schema even when validators say it's fine.
What BreadcrumbList actually does
Three real functions, in order of practical impact:
Function 1: Replaces URL display in mobile SERPs. On mobile, Google replaces the URL line of your search result with the breadcrumb trail when BreadcrumbList schema is present and valid. Instead of seeing yoursite.com › blog › post-slug (the URL path), users see Home › Category › Article Title (the breadcrumb). This is the most visible benefit and the one most easily verified — search for one of your pages on mobile and check the result display.
Function 2: Helps Google understand site hierarchy. Even without producing the visible SERP feature, BreadcrumbList signals the logical structure of your site to Google's crawler and indexing systems. Pages with clear breadcrumb schema get categorized more accurately, which affects how they're surfaced for queries related to the category.
Function 3: Contributes to topical authority signaling. When Google sees a page within a clear breadcrumb hierarchy (Home → Category → Subcategory → Article), it interprets the page as part of an organized content cluster rather than an orphan article. This contributes to the topical authority signals AI engines use when deciding which sources to cite.
What BreadcrumbList doesn't do:
- It doesn't replace site navigation. Breadcrumbs should match the logical hierarchy, not your menu structure.
- It doesn't help with knowledge panels or featured snippets directly.
- It doesn't appear in desktop SERPs as a visual feature in most cases — the visible benefit is mobile-focused.
The two acceptable formats
Google accepts BreadcrumbList in two slightly different JSON-LD formats. Both validate; the choice matters for maintainability rather than correctness.
Format 1: Long form with Thing wrapper.
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"item": {
"@type": "Thing",
"@id": "https://yoursite.com/",
"name": "Home"
}
},
{
"@type": "ListItem",
"position": 2,
"item": {
"@type": "Thing",
"@id": "https://yoursite.com/blog",
"name": "Blog"
}
},
{
"@type": "ListItem",
"position": 3,
"item": {
"@type": "Thing",
"@id": "https://yoursite.com/blog/article-slug",
"name": "Article Title"
}
}
]
}
Format 2: Short form with name and URL string.
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://yoursite.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://yoursite.com/blog"
},
{
"@type": "ListItem",
"position": 3,
"name": "Article Title",
"item": "https://yoursite.com/blog/article-slug"
}
]
}
When to use each:
The short form is preferred for most implementations. It's more compact, easier to generate programmatically, and produces identical rich result behavior. Most modern schema generators output this format by default.
The long form becomes useful when you're already using @graph syntax to connect multiple schema types and want to reference breadcrumb items by @id from other schema entities. If you have a complex schema graph where Article, Person, and Organization all reference each other, matching the breadcrumb items into that pattern with @id references can make the graph more coherent.
For 90% of content sites: use the short form. It's cleaner.
Position numbering rules
The single most common error in BreadcrumbList implementation. The rules:
Position 1 is the top of the hierarchy. This is almost always your homepage. Some implementations skip the homepage and start at the category page — that's technically valid in schema.org's spec but causes display issues in Google's SERP because Google expects the first position to be the root.
Positions must be sequential integers starting at 1. Skipping numbers (1, 3, 4) or starting at 0 both cause validation issues in some parsers and rich result failures in Google's.
The last position is the current page. This page should NOT be a clickable link in the visible breadcrumb on the page (browsers shouldn't navigate to it because you're already there), but the schema still includes the URL.
Hierarchy depth: 2-5 levels typically. A single-item BreadcrumbList provides no rich result benefit because there's no trail to display. More than 5 levels usually indicates a site structure that's too deep — consider consolidating categories.
Common position errors:
- Starting position at 0 (some developers default to zero-indexed arrays and forget to add 1)
- Skipping the homepage position
- Numbering items out of order (the array order should match position order, but parsers will read the position field — keep them aligned to avoid confusion)
- Using string positions ("1", "2") instead of integers (1, 2)
The complete working example
Here's the full pattern I recommend for blog posts, using the short form with @graph integration so it connects to your other schema:
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "BreadcrumbList",
"@id": "https://yoursite.com/blog/article-slug#breadcrumb",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://yoursite.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://yoursite.com/blog"
},
{
"@type": "ListItem",
"position": 3,
"name": "AEO Optimization",
"item": "https://yoursite.com/blog/category/aeo"
},
{
"@type": "ListItem",
"position": 4,
"name": "BreadcrumbList Schema Implementation",
"item": "https://yoursite.com/blog/breadcrumb-schema-implementation"
}
]
},
{
"@type": "Article",
"@id": "https://yoursite.com/blog/article-slug#article",
"headline": "BreadcrumbList Schema Implementation",
"breadcrumb": {
"@id": "https://yoursite.com/blog/article-slug#breadcrumb"
}
}
]
}
The Article references the BreadcrumbList via @id in the breadcrumb field. This connection isn't strictly required for validation, but it strengthens the relationship for Google's parser. I covered the broader Article schema pattern in the Article schema E-E-A-T tutorial; BreadcrumbList integrates naturally into the same graph.
Implementation by platform
The schema is identical across platforms, but how you generate and deploy it varies significantly.
WordPress
Yoast SEO and RankMath both generate BreadcrumbList schema automatically if you've configured breadcrumb settings in the plugin and added the breadcrumb display to your theme.
For Yoast:
- SEO → Search Appearance → Breadcrumbs → Enable
- Verify your theme calls
yoast_breadcrumb()in the appropriate template files (single.php, page.php, etc.) - The plugin outputs BreadcrumbList JSON-LD automatically; check page source to verify
For RankMath:
- Titles & Meta → Misc → Breadcrumbs → Enable
- Configure the homepage label and separator
- Add the breadcrumb display via shortcode
[rank_math_breadcrumb]or PHP<?php do_action( 'rank_math/frontend/breadcrumb' ); ?>in templates - Schema is generated automatically
Common WordPress problems:
- Both Yoast AND RankMath active simultaneously → duplicate BreadcrumbList blocks. Disable breadcrumb features in one plugin.
- Theme has hardcoded breadcrumb HTML but no schema → schema must be generated separately or via the SEO plugin
- Custom post types without proper hierarchy settings → breadcrumbs default to flat (Home → Post) instead of the proper category structure
Next.js (App Router and Pages Router)
For Next.js sites, the cleanest pattern is generating the schema server-side in your page component and rendering it inline.
// app/blog/[slug]/page.tsx (App Router) or similar pattern
function generateBreadcrumbSchema(post) {
return {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://yoursite.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://yoursite.com/blog"
},
{
"@type": "ListItem",
"position": 3,
"name": post.category,
"item": `https://yoursite.com/blog/category/${post.categorySlug}`
},
{
"@type": "ListItem",
"position": 4,
"name": post.title,
"item": `https://yoursite.com/blog/${post.slug}`
}
]
};
}
export default function BlogPost({ post }) {
const breadcrumbSchema = generateBreadcrumbSchema(post);
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
/>
{/* rest of your component */}
</>
);
}
Common Next.js problems:
- Client-side rendering the schema with
useEffectinstead of server-rendering → bots don't see it - Using
next-seoplugin defaults without customizing for your URL structure → breadcrumb positions and names don't match the actual site hierarchy - Generating breadcrumbs from the URL path alone → loses the visible category names that should appear in breadcrumb display
Webflow
Webflow doesn't generate BreadcrumbList schema natively. The typical implementation:
- Add Custom Code embed inside the page (Page Settings → Custom Code → Before
</body>tag) - Hardcode the BreadcrumbList JSON-LD for each page or template
- For CMS Collection pages, use Webflow's CMS field bindings inside the script tag
The challenge is that Webflow's CMS bindings work in static content but require JavaScript for dynamic schema generation, and JavaScript-rendered schema doesn't work reliably for SEO.
The recommended pattern: use Webflow's templating in the Page Settings rather than relying on JavaScript:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://yoursite.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://yoursite.com/blog"
},
{
"@type": "ListItem",
"position": 3,
"name": "{{Name}}",
"item": "https://yoursite.com/blog/{{Slug}}"
}
]
}
</script>
Where {{Name}} and {{Slug}} are Webflow CMS field bindings that get rendered server-side.
Headless CMS (Contentful, Sanity, Strapi)
For headless setups, the BreadcrumbList generation happens in the frontend rendering layer (Next.js, Gatsby, Astro, etc.), pulling category and title data from the CMS.
The pattern is identical to the Next.js example above, but with the data source being the CMS API rather than a local content directory. The key implementation detail: make sure category and breadcrumb labels are explicit fields in your content model, not derived from slugs. Slug-derived labels often produce ugly breadcrumbs ("aeo-tools" instead of "AEO Tools").
Static site generators (Astro, Hugo, Eleventy)
These usually have templating systems that can generate BreadcrumbList from page metadata. For Astro:
---
const breadcrumbs = [
{ name: "Home", url: "https://yoursite.com/" },
{ name: "Blog", url: "https://yoursite.com/blog" },
{ name: Astro.props.title, url: Astro.url.href }
];
const schema = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": breadcrumbs.map((item, i) => ({
"@type": "ListItem",
"position": i + 1,
"name": item.name,
"item": item.url
}))
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
Hugo and Eleventy have similar patterns using their respective templating languages.
Six common mistakes and fixes
Mistake 1: BreadcrumbList doesn't match the visible breadcrumb on the page. Schema declares Home → Blog → Article; visible breadcrumb on the page shows Home → Article (skipping Blog). Google's parser checks for this consistency. Either update the visible breadcrumb to match or update the schema to match — don't leave them disagreeing.
Fix: Sync them. The visible breadcrumb HTML and the schema should describe the same hierarchy with the same labels.
Mistake 2: Multiple BreadcrumbList blocks on the same page. Common when an SEO plugin generates one and a theme widget generates another. The parser doesn't know which to use and may show none.
Fix: View page source, search for "@type": "BreadcrumbList". If there are multiple, disable the redundant sources.
Mistake 3: Single-item BreadcrumbList. Page has only one item in the breadcrumb (typically just the page itself). This validates but produces no rich result because there's no trail to display.
Fix: Ensure your hierarchy includes at least 2 levels — homepage plus the current page minimum. For category-less pages, the pattern Home → Page Title is acceptable; for blog posts, Home → Blog → Article is the minimum useful pattern.
Mistake 4: Including external URLs in the breadcrumb chain. The breadcrumb should reflect the path within your site only. External links break the "logical hierarchy from your site root" pattern that the parser expects.
Fix: Remove any external URLs from breadcrumb items. If you legitimately want to reference an external resource on the page, do it in body content, not in breadcrumbs.
Mistake 5: JavaScript-rendered breadcrumb schema. Schema added via React state, jQuery DOM insertion, or any client-side method after page load. Bots that don't execute JavaScript reliably (which includes some of Google's crawler modes) don't see the schema.
Fix: Render schema server-side, in the initial HTML response. Verify by viewing page source (not Inspector) — the schema should be present in the raw HTML.
Mistake 6: Position field as string instead of integer. Some schema generators output "position": "1" (string) instead of "position": 1 (integer). JSON validators accept both but Google's parser specifically requires integers for position.
Fix: Ensure your schema generation outputs integers. In JavaScript, this means parseInt(i, 10) or numeric literals, not string concatenation.
How to verify it's working
Three checks, in order:
Check 1: JSON validates. Paste your full BreadcrumbList JSON into the JSON-LD playground at jsonld.org/playground. It should parse without errors. If JSON itself is broken, nothing else matters.
Check 2: Schema validates against schema.org. Run your page through validator.schema.org. It should report no errors. If errors appear, fix those before continuing.
Check 3: Google's Rich Results Test passes. This is the one that matters for SEO. Run the URL through Google's Rich Results Test at search.google.com/test/rich-results. The Breadcrumbs section should appear with all your items listed and no errors. If errors appear, the test output tells you exactly which field is problematic.
Check 4 (post-deployment): Mobile SERP display. After deploying and waiting 2-3 weeks for Google to re-crawl, search for one of your pages on a mobile device. The URL line in the result should show your breadcrumb trail instead of the raw URL path. If it doesn't, either the schema isn't being detected (re-check the validation steps) or Google hasn't re-crawled the page yet (use URL Inspection's Request Indexing to speed it up).
Connection to AI Overview citation
BreadcrumbList isn't a major factor in AI Overview citation in the way FAQPage or Article+Person are, but it's not zero-impact either. SE Ranking's analysis found about 65% of pages cited by Google AI Mode include structured data markup. BreadcrumbList is part of the typical "structured data is present" signal that contributes to that 65% figure.
More substantively: BreadcrumbList helps the AI parser understand which content cluster your page belongs to. When a query about "AEO optimization" triggers the citation evaluation, pages within a clear /blog/aeo hierarchy that's confirmed by BreadcrumbList schema get categorized correctly. Pages without breadcrumb schema rely on URL inference, which is less reliable.
If you've worked through the seven failure patterns for AI Overview citation and ruled out the major issues, adding BreadcrumbList is a small marginal improvement worth implementing — but it's not the difference between citation and no-citation in most cases.
FAQ
Do I need BreadcrumbList schema if my site doesn't show visible breadcrumbs to users?
Should the last breadcrumb item link to the current page or not?
How deep should my breadcrumb hierarchy go?
Can BreadcrumbList schema hurt my SEO if implemented incorrectly?
What's the difference between BreadcrumbList schema and HTML breadcrumb navigation?
If I use breadcrumbs in my site navigation, do I still need separate BreadcrumbList schema?
Can BreadcrumbList replace URL pattern signals like clean URLs and category-based site structure?
How long does it take for breadcrumb-enabled SERP results to appear after adding the schema?
Sources cited in this piece
- Schema.org official BreadcrumbList specification (schema.org/BreadcrumbList)
- Google's structured data documentation for breadcrumbs (developers.google.com/search/docs)
- Google's Rich Results Test parsing behavior (observable from running schemas through the public tool)
- SE Ranking (post-January 2026): Gemini 3 analysis (65% schema rate among AI-cited pages)
- WordPress plugin documentation (Yoast, RankMath) for BreadcrumbList output specifics
If you've implemented BreadcrumbList and the mobile SERP isn't showing the breadcrumb trail after 4+ weeks, send the page URL on X at @edgrows. I'll run it through the validators and identify which of the common mistakes is most likely.