Breadcrumb schema (BreadcrumbList JSON-LD) tells search engines the hierarchical path to a page, enabling breadcrumb trails in Google Search results. WordPress generates breadcrumbs structurally but does not output JSON-LD schema by default — adding it requires a few lines of PHP that read the current page’s ancestors.
Problem: Google Search Console shows WordPress category, tag, and custom taxonomy archive pages in search results but without breadcrumb rich results — the HTML breadcrumb navigation is not marked up with schema that Google can parse.
Solution: Output a BreadcrumbList JSON-LD block in wp_head — build the item array from get_the_archive_title(), get_term_parents_list(), and get_permalink(), then encode and echo it. For single posts, walk up the category hierarchy. Validate with Google's Rich Results Test.
The examples below generate BreadcrumbList JSON-LD for posts (using categories), pages (using parent pages), WooCommerce products, and custom post types with custom taxonomies.
<?php
add_action( 'wp_head', 'myplugin_output_breadcrumb_schema' );
function myplugin_output_breadcrumb_schema(): void {
if ( ! is_singular() && ! is_archive() && ! is_home() ) return;
$items = myplugin_build_breadcrumb_items();
if ( count( $items ) < 2 ) return; // no point showing just "Home"
$list_items = [];
foreach ( $items as $position => $item ) {
$list_items[] = [
'@type' => 'ListItem',
'position' => $position + 1,
'name' => $item['name'],
'item' => $item['url'],
];
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $list_items,
];
echo '<script type="application/ld+json">'
. wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES )
. '</script>' . "
";
}
function myplugin_build_breadcrumb_items(): array {
$items = [];
$items[] = [ 'name' => get_bloginfo( 'name' ), 'url' => home_url( '/' ) ];
if ( is_singular( 'post' ) ) {
// Add primary category
$cats = get_the_category();
if ( ! empty( $cats ) ) {
$cat = $cats[0];
// Walk up category ancestors
$ancestors = array_reverse( get_ancestors( $cat->term_id, 'category' ) );
foreach ( $ancestors as $ancestor_id ) {
$anc = get_term( $ancestor_id, 'category' );
$items[] = [ 'name' => $anc->name, 'url' => get_term_link( $anc ) ];
}
$items[] = [ 'name' => $cat->name, 'url' => get_term_link( $cat ) ];
}
$items[] = [ 'name' => get_the_title(), 'url' => get_permalink() ];
} elseif ( is_singular( 'page' ) ) {
// Walk up page parent chain
$ancestors = array_reverse( get_ancestors( get_the_ID(), 'page' ) );
foreach ( $ancestors as $ancestor_id ) {
$items[] = [ 'name' => get_the_title( $ancestor_id ), 'url' => get_permalink( $ancestor_id ) ];
}
$items[] = [ 'name' => get_the_title(), 'url' => get_permalink() ];
} elseif ( is_singular( 'product' ) ) {
$terms = get_the_terms( get_the_ID(), 'product_cat' );
if ( $terms && ! is_wp_error( $terms ) ) {
$cat = $terms[0];
$items[] = [ 'name' => $cat->name, 'url' => get_term_link( $cat ) ];
}
$items[] = [ 'name' => get_the_title(), 'url' => get_permalink() ];
}
return $items;
}
Also output a visible breadcrumb trail using the same data:
<?php
// Output an HTML breadcrumb trail — uses the same data as the schema
function myplugin_render_breadcrumbs(): void {
$items = myplugin_build_breadcrumb_items();
if ( count( $items ) < 2 ) return;
echo '<nav class="breadcrumbs" aria-label="' . esc_attr__( 'Breadcrumb', 'myplugin' ) . '">';
echo '<ol itemscope itemtype="https://schema.org/BreadcrumbList">';
$last = count( $items ) - 1;
foreach ( $items as $pos => $item ) {
$is_last = ( $pos === $last );
echo '<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">';
if ( ! $is_last ) {
printf( '<a itemprop="item" href="%s"><span itemprop="name">%s</span></a>',
esc_url( $item['url'] ), esc_html( $item['name'] ) );
} else {
printf( '<span itemprop="name" aria-current="page">%s</span>', esc_html( $item['name'] ) );
}
echo '<meta itemprop="position" content="' . ( $pos + 1 ) . '">';
echo '</li>';
if ( ! $is_last ) echo '<li aria-hidden="true">›</li>';
}
echo '</ol></nav>';
}
NOTE: Test your breadcrumb schema with Google's Rich Results Test. The JSON-LD and microdata outputs are equivalent — use JSON-LD (the <script> block) if you want to keep markup semantic and schema separate, which is Google's recommended approach.