SEO PHP Intermediate

Add Breadcrumb Schema Markup Without a Plugin

Last updated: May 6, 2026

Breadcrumbs serve two purposes on a WordPress site: they help visitors understand where they are within your site’s hierarchy, and they give Google structured data it can use to display breadcrumb trails directly in search results. When Google shows a breadcrumb in a SERP snippet, for example nahnuplugins.com › SEO › Auto-Submit Sitemap, it typically pulls that from BreadcrumbList schema rather than the raw URL. This snippet adds that schema automatically to your page head with no plugin dependency.

The Code

Add this to your functions.php or a site-specific plugin. It hooks into wp_head and outputs a JSON-LD BreadcrumbList script block for singular posts, taxonomy archives, and custom post type archives.

How It Works

The snippet builds an array of ListItem objects, each representing one level of the breadcrumb trail. The home page is always the first item. After that, the logic branches based on the current page type.

On singular posts, it checks for a primary category using get_the_terms(). If the post belongs to a category, that category is inserted as the middle crumb between Home and the post title. This mirrors the breadcrumb structure most themes display visually. If the post has no category, such as a custom post type without a taxonomy, the middle crumb is skipped and the trail goes directly from Home to the post.

On category and taxonomy archives, the queried term is added as the final crumb after Home. On custom post type archives, the post type’s label and archive URL are used instead.

The if ( count( $items ) < 2 ) return; guard prevents outputting schema for pages where only the Home item was built, for example, the front page itself, which is already excluded at the top of the function.

The final schema is encoded using wp_json_encode() with JSON_UNESCAPED_SLASHES and JSON_UNESCAPED_UNICODE flags to keep URLs readable and avoid unnecessary escaping of non-ASCII characters.

Verifying the Output

After adding this snippet, visit any post on your site and view the page source. You should see a <script type="application/ld+json"> block near the top of the <head> containing your breadcrumb data. You can also paste the URL into Google’s Rich Results Test tool at search.google.com/test/rich-results to confirm Google can parse the schema and that it’s eligible for breadcrumb display in search results.

When to Use This

This snippet is most valuable on sites without an SEO plugin that generates breadcrumb schema automatically, or on sites where you want lightweight schema output without relying on a plugin’s full schema suite. If you’re already using a plugin like SlimSEO with schema support, check whether it outputs BreadcrumbList schema before adding this to avoid duplicates.

It pairs well with a visual breadcrumb shortcode or block in your theme, the schema and the visual breadcrumb trail can coexist and serve complementary purposes for search engines and users respectively.

functions.php
add_action( 'wp_head', function() {
    if ( is_front_page() || is_home() ) return;

    $items = [];
    $pos   = 1;

    // Home
    $items[] = [
        '@type'    => 'ListItem',
        'position' => $pos++,
        'name'     => get_bloginfo( 'name' ),
        'item'     => home_url( '/' ),
    ];

    if ( is_singular() ) {
        // Category middle crumb for posts
        $terms = get_the_terms( get_the_ID(), 'category' );
        if ( $terms && ! is_wp_error( $terms ) ) {
            $term    = array_shift( $terms );
            $items[] = [
                '@type'    => 'ListItem',
                'position' => $pos++,
                'name'     => $term->name,
                'item'     => get_term_link( $term ),
            ];
        }
        $items[] = [
            '@type'    => 'ListItem',
            'position' => $pos++,
            'name'     => get_the_title(),
            'item'     => get_permalink(),
        ];
    } elseif ( is_category() || is_tax() ) {
        $term    = get_queried_object();
        $items[] = [
            '@type'    => 'ListItem',
            'position' => $pos++,
            'name'     => $term->name,
            'item'     => get_term_link( $term ),
        ];
    } elseif ( is_post_type_archive() ) {
        $obj     = get_queried_object();
        $items[] = [
            '@type'    => 'ListItem',
            'position' => $pos++,
            'name'     => $obj->label,
            'item'     => get_post_type_archive_link( $obj->name ),
        ];
    }

    if ( count( $items ) < 2 ) return;

    $schema = [
        '@context'        => 'https://schema.org',
        '@type'           => 'BreadcrumbList',
        'itemListElement' => $items,
    ];

    echo '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>' . "\n";
} );

Built by Nahnu Plugins

Need something more powerful than a snippet?

Our commercial plugins go further, built for serious WordPress sites with full support, updates, and documentation included.

Browse All Plugins →

This website uses cookies to enhance your browsing experience and ensure the site functions properly. By continuing to use this site, you acknowledge and accept our use of cookies.

Accept All Accept Required Only