# Breadcrumb Schema Best Practices

**Last Updated:** 2026-01-20

Comprehensive guide for implementing BreadcrumbList structured data correctly to avoid duplicate detection issues in Google Search Console.

## Overview

BreadcrumbList schema helps search engines understand page hierarchy and can enable breadcrumb rich results in Google Search. This guide covers best practices for implementing breadcrumb schema without duplicates.

## The Duplicate Detection Problem

### What Happens

Google Search Console may detect **2 breadcrumb elements** when both JSON-LD and Microdata formats are present on the same page:

- **JSON-LD BreadcrumbList** - Structured data in `<script type="application/ld+json">` tags
- **Microdata BreadcrumbList** - Structured data using `itemscope`, `itemtype`, and `itemprop` attributes

### Why It Happens

- **Multiple implementations**: Both JSON-LD and Microdata breadcrumb schemas output on the same page
- **Theme/plugin conflicts**: Different systems generating breadcrumbs independently
- **Legacy code**: Older Microdata implementation alongside newer JSON-LD implementation

### Impact

- **GSC warnings**: "2 valid items detected" for Breadcrumbs
- **Confusion**: Unclear which schema Google will use
- **Potential issues**: Conflicting or incomplete data across formats

## Best Practice: Use JSON-LD Only

### Why JSON-LD is Preferred

1. **Google's recommendation**: JSON-LD is Google's preferred format for structured data
2. **Easier maintenance**: Centralized schema generation, less error-prone
3. **Cleaner HTML**: No inline attributes cluttering markup
4. **Better validation**: Easier to validate and debug
5. **Modern standard**: Industry best practice as of 2026

### Implementation Strategy

**For Blog Pages:**
- ✅ Use JSON-LD BreadcrumbList (via `blog-schema-generator.php`)
- ✅ Disable Microdata in Breadcrumbs component (`$include_schema = false`)
- ✅ Single source of truth for breadcrumb schema

**For Other Pages:**
- Can use either JSON-LD or Microdata (but not both)
- Prefer JSON-LD when possible
- Ensure only one format is used per page

## Current Implementation

### Blog Posts

**JSON-LD Schema** (in `<head>`):
- Generated by: `v2/config/blog-schema-generator.php`
- Function: `generate_blog_schema('post', $post)`
- Output: JSON-LD script tag with BreadcrumbList

**Microdata Schema** (disabled):
- Component: `v2/components/blog/Breadcrumbs.php`
- Setting: `$include_schema = false` in `post.php`
- Result: Visual breadcrumbs without schema markup

**Code Example:**
```php
// In v2/pages/blog/post.php
$items = $breadcrumb_items;
$include_schema = false; // JSON-LD already provided via render_blog_schema()
include __DIR__ . '/../../components/blog/Breadcrumbs.php';
```

### Topic Hub Pages

Same approach as blog posts:
- JSON-LD via `render_blog_schema('topic_hub', $data)`
- Microdata disabled in Breadcrumbs component

## Schema Structure Requirements

### Required Properties

A valid BreadcrumbList schema must include:

1. **`@type`**: `"BreadcrumbList"`
2. **`itemListElement`**: Array of ListItem objects
3. **Each ListItem must have**:
   - `@type`: `"ListItem"`
   - `position`: Integer (1, 2, 3, etc.)
   - `name`: String (display name)
   - `item`: URL (required for all items except last, which can omit if matches page URL)

### Example Structure

```json
{
  "@type": "BreadcrumbList",
  "@id": "https://www.ordio.com/insights/ratgeber/arbeitsstunden-pro-monat/#breadcrumb",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.ordio.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Blog",
      "item": "https://www.ordio.com/insights/"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Ratgeber",
      "item": "https://www.ordio.com/insights/ratgeber/"
    },
    {
      "@type": "ListItem",
      "position": 4,
      "name": "Arbeitsstunden pro Monat",
      "item": "https://www.ordio.com/insights/ratgeber/arbeitsstunden-pro-monat/"
    }
  ]
}
```

## Troubleshooting

### Issue: GSC Shows "2 valid items detected"

**Symptoms:**
- Google Search Console reports duplicate breadcrumb elements
- Schema validator may show 1 or 2 depending on parsing logic

**Solution:**
1. Check page source for both JSON-LD and Microdata breadcrumbs
2. Disable Microdata if JSON-LD is present (preferred)
3. Or disable JSON-LD if Microdata is already working (less preferred)

**For Blog Pages:**
```php
// Set $include_schema = false when including Breadcrumbs component
$include_schema = false;
include __DIR__ . '/../../components/blog/Breadcrumbs.php';
```

### Issue: Breadcrumb Schema Missing

**Symptoms:**
- GSC shows no breadcrumb items
- Schema validator shows no BreadcrumbList

**Solution:**
1. Verify `render_blog_schema()` is called in blog post template
2. Check that `blog-schema-generator.php` generates BreadcrumbList
3. Validate JSON-LD structure with Rich Results Test

### Issue: Invalid Breadcrumb Structure

**Symptoms:**
- GSC shows errors for breadcrumb schema
- Schema validator reports missing required fields

**Solution:**
1. Ensure all ListItems have `@type`, `position`, `name`
2. Verify all non-last items have `item` URL
3. Check that positions are sequential (1, 2, 3, etc.)
4. Validate with test script: `php v2/scripts/blog/test-breadcrumb-schema.php`

## Testing

### Manual Testing

1. **View Page Source**
   - Search for `"@type": "BreadcrumbList"` - should find exactly 1 instance
   - Search for `itemtype="BreadcrumbList"` - should find 0 instances (for blog pages)

2. **Google Search Console**
   - Use URL Inspection tool
   - Check "Breadcrumbs" section
   - Should show "1 valid item detected" (not 2)

3. **Schema Markup Validator**
   - Test URL: `https://validator.schema.org/`
   - Should show 1 BreadcrumbList element
   - Should show 0 errors, 0 warnings

### Automated Testing

**Test Script**: `v2/scripts/blog/test-breadcrumb-schema.php`

```bash
# Test specific post
php v2/scripts/blog/test-breadcrumb-schema.php ratgeber arbeitsstunden-pro-monat

# Test sample of posts
php v2/scripts/blog/test-breadcrumb-schema.php --sample 10

# Test all posts
php v2/scripts/blog/test-breadcrumb-schema.php --all
```

**What It Checks:**
- JSON-LD BreadcrumbList presence
- Schema structure validation
- Required properties verification
- Microdata disabled status

## Component Usage

### Breadcrumbs Component

**File**: `v2/components/blog/Breadcrumbs.php`

**Parameters:**
- `$items` (array, required) - Array of breadcrumb items with `name` and `url`
- `$include_schema` (bool, optional) - Whether to include Microdata schema (default: `true`)

**Usage for Blog Pages:**
```php
$items = [
    ['name' => 'Home', 'url' => '/'],
    ['name' => 'Blog', 'url' => '/insights/'],
    ['name' => 'Post Title', 'url' => '/insights/category/post-slug/']
];
$include_schema = false; // Disable Microdata - JSON-LD already provided
include __DIR__ . '/../../components/blog/Breadcrumbs.php';
```

**Usage for Other Pages (if needed):**
```php
$items = [
    ['name' => 'Home', 'url' => '/'],
    ['name' => 'Page Name', 'url' => '/page/']
];
// $include_schema defaults to true if not set
include __DIR__ . '/../../components/blog/Breadcrumbs.php';
```

## Google's Syntax Graph Merge

### What It Is

Google supports "Syntax Graph Merge" - connecting structured data across formats using consistent identifiers (`@id` in JSON-LD matching identifiers in Microdata).

### When to Use

- **Advanced use cases**: Only when necessary to connect different schema types
- **Not recommended**: For same schema type (like BreadcrumbList)
- **Best practice**: Use one format per schema type

### For BreadcrumbList

- **Don't mix**: Use either JSON-LD OR Microdata, not both
- **Prefer JSON-LD**: Google's recommended format
- **Avoid duplicates**: Prevents GSC warnings and confusion

## Related Documentation

- [Canonical Tags Best Practices](./CANONICAL_TAGS_BEST_PRACTICES.md)
- [Blog Schema Generator](../../config/blog-schema-generator.php)
- [Blog Templates Components](../../content/blog/components/BLOG_TOC_COMPONENT.md)

## References

- [Google: Breadcrumb Structured Data](https://developers.google.com/search/docs/appearance/structured-data/breadcrumb)
- [Google: Structured Data Intro](https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data)
- [Schema.org: BreadcrumbList](https://schema.org/BreadcrumbList)
