Breadcrumb Schema JSON-LD for WordPress SEO

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.

 $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 '' . "
";
}

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:

';
    echo '
    '; $last = count( $items ) - 1; foreach ( $items as $pos => $item ) { $is_last = ( $pos === $last ); echo '
  1. '; if ( ! $is_last ) { printf( '%s', esc_url( $item['url'] ), esc_html( $item['name'] ) ); } else { printf( '%s', esc_html( $item['name'] ) ); } echo ''; echo '
  2. '; if ( ! $is_last ) echo ''; } echo '
'; }

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.

Leave Comment

Your email address will not be published. Required fields are marked *