How to Create Custom Breadcrumbs in WordPress Without a Plugin

Breadcrumbs are essential for enhancing user navigation on your website. They provide a clear path for visitors to understand their location within your site’s hierarchy. While there are several plugins available for adding breadcrumbs in WordPress, using custom code can be more efficient and lightweight. In this guide, I’ll show you how to create custom breadcrumbs without relying on additional plugins.

First, open your WordPress theme’s functions.php file. You can find this file in your theme directory. Add the following custom code snippet to the file:

// Start SV Breadcrumbs
function sv_custom_breadcrumbs()
{
    ob_start();
    // Settings
    $separator = '>';
    $breadcrums_id = 'sv_custom_breadcrumbs';
    $breadcrums_class = 'sv_custom_breadcrumbs';
    $home_title = 'Home';

    // If you have any custom post types with custom taxonomies, put the taxonomy name below (e.g. product_cat)
    $custom_taxonomy = 'product_cat';

    // Get the query & post information
    global $post, $wp_query;

    // Do not display on the homepage
    if (!is_front_page()) {
        // Build the breadcrums
        echo '<div id="' . $breadcrums_id . '" class="' . $breadcrums_class . '">';
        if (is_search()) {
            echo '<h2><strong>' . get_search_query() . '</strong></h2> <br />';
        } else {
            echo '<h2><strong>' . get_the_title() . '</strong></h2> <br />';
        }
        echo '<ul>';

        // Home page
        echo '<li class="item-home"><a class="bread-link bread-home" href="' . get_home_url() . '" >' . $home_title . '</a></li>';
        // echo '<li class="separator separator-home"> ' . $separator . ' </li>';

        if (is_archive() && !is_tax() && !is_category() && !is_tag()) {

            echo '<li class="item-current item-archive"><h2><strong class="bread-current bread-archive">' . post_type_archive_title('', false) . '</strong></h2></li>';

        } else if (is_archive() && is_tax() && !is_category() && !is_tag()) {

            // If post is a custom post type
            $post_type = get_post_type();

            // If it is a custom post type display name and link
            if ($post_type != 'post') {

                $post_type_object = get_post_type_object($post_type);
                $post_type_archive = get_post_type_archive_link($post_type);

                echo '<li class="item-cat item-custom-post-type-' . $post_type . '"><a class="bread-cat bread-custom-post-type-' . $post_type . '" href="' . $post_type_archive . '" title="' . $post_type_object->labels->name . '">' . $post_type_object->labels->name . '</a></li>';
                // echo '<li class="separator"> ' . $separator . ' </li>';

            }

            $custom_tax_name = get_queried_object()->name;
            echo '<li class="item-current item-archive"><h2><strong class="bread-current bread-archive">' . $custom_tax_name . '</strong></h2></li>';

        } else if (is_single()) {

            // If post is a custom post type
            $post_type = get_post_type();

            // If it is a custom post type display name and link
            if ($post_type != 'post') {

                $post_type_object = get_post_type_object($post_type);
                $post_type_archive = get_post_type_archive_link($post_type);

                echo '<li class="item-cat item-custom-post-type-' . $post_type . '"><a class="bread-cat bread-custom-post-type-' . $post_type . '" href="' . $post_type_archive . '" title="' . $post_type_object->labels->name . '">' . $post_type_object->labels->name . '</a></li>';
                // echo '<li class="separator"> ' . $separator . ' </li>';

            }

            // If it's a custom post type within a custom taxonomy
            $taxonomy_exists = taxonomy_exists($custom_taxonomy);
            if (empty($last_category) && !empty($custom_taxonomy) && $taxonomy_exists) {

                $taxonomy_terms = get_the_terms($post->ID, $custom_taxonomy);
                $cat_id = $taxonomy_terms[0]->term_id;
                $cat_nicename = $taxonomy_terms[0]->slug;
                $cat_link = get_term_link($taxonomy_terms[0]->term_id, $custom_taxonomy);
                $cat_name = $taxonomy_terms[0]->name;

            }

            // Check if the post is in a category
            if (!empty($last_category)) {
                // echo $cat_display;
                echo '<li class="item-current item-' . $post->ID . '"><h2><strong class="bread-current bread-' . $post->ID . '" title="' . get_the_title() . '">' . get_the_title() . '</strong></h2></li>';

                // Else if post is in a custom taxonomy
            } else if (!empty($cat_id)) {

                echo '<li class="item-cat item-cat-' . $cat_id . ' item-cat-' . $cat_nicename . '"><a class="bread-cat bread-cat-' . $cat_id . ' bread-cat-' . $cat_nicename . '" href="' . $cat_link . '" title="' . $cat_name . '">' . $cat_name . '</a></li>';
                // echo '<li class="separator"> ' . $separator . ' </li>';
                echo '<li class="item-current item-' . $post->ID . '"><h2><strong class="bread-current bread-' . $post->ID . '" title="' . get_the_title() . '">' . get_the_title() . '</strong></h2></li>';

            } else {

                echo '<li class="item-current item-' . $post->ID . '"><h2><strong class="bread-current bread-' . $post->ID . '" title="' . get_the_title() . '">' . get_the_title() . '</strong></h></li>';

            }

        } else if (is_category()) {

            // Category page
            echo '<li class="item-current item-cat"><h2><strong class="bread-current bread-cat">' . single_cat_title('', false) . '</strong></h2></li>';

        } else if (is_page()) {

            // Standard page
            if ($post->post_parent) {

                // If child page, get parents 
                $anc = get_post_ancestors($post->ID);

                // Get parents in the right order
                $anc = array_reverse($anc);

                // Parent page loop
                if (!isset($parents))
                    $parents = null;
                foreach ($anc as $ancestor) {
                    $parents .= '<li class="item-parent item-parent-' . $ancestor . '"><a class="bread-parent bread-parent-' . $ancestor . '" href="' . get_permalink($ancestor) . '" >' . get_the_title($ancestor) . '</a></li>';
                    // $parents .= '<li class="separator separator-' . $ancestor . '"> ' . $separator . ' </li>';
                }

                // Display parent pages
                echo $parents;

                // Current page
                echo '<li class="item-current item-' . $post->ID . '"><h2><strong title="' . get_the_title() . '"> ' . get_the_title() . '</strong></h2></li>';

            } else {

                // Just display current page if not parents
                echo '<li class="item-current item-' . $post->ID . '"><h2><strong class="bread-current bread-' . $post->ID . '"> ' . get_the_title() . '</strong></h2></li>';

            }

        } else if (is_tag()) {

            // Tag page

            // Get tag information
            $term_id = get_query_var('tag_id');
            $taxonomy = 'post_tag';
            $args = 'include=' . $term_id;
            $terms = get_terms($taxonomy, $args);
            $get_term_id = $terms[0]->term_id;
            $get_term_slug = $terms[0]->slug;
            $get_term_name = $terms[0]->name;

            // Display the tag name
            echo '<li class="item-current item-tag-' . $get_term_id . ' item-tag-' . $get_term_slug . '"><h2><strong class="bread-current bread-tag-' . $get_term_id . ' bread-tag-' . $get_term_slug . '">' . $get_term_name . '</strong></h2></li>';

        } elseif (is_day()) {

            // Day archive

            // Year link
            echo '<li class="item-year item-year-' . get_the_time('Y') . '"><a class="bread-year bread-year-' . get_the_time('Y') . '" href="' . get_year_link(get_the_time('Y')) . '" title="' . get_the_time('Y') . '">' . get_the_time('Y') . ' Archives</a></li>';
            // echo '<li class="separator separator-' . get_the_time('Y') . '"> ' . $separator . ' </li>';

            // Month link
            echo '<li class="item-month item-month-' . get_the_time('m') . '"><a class="bread-month bread-month-' . get_the_time('m') . '" href="' . get_month_link(get_the_time('Y'), get_the_time('m')) . '" title="' . get_the_time('M') . '">' . get_the_time('M') . ' Archives</a></li>';
            // echo '<li class="separator separator-' . get_the_time('m') . '"> ' . $separator . ' </li>';

            // Day display
            echo '<li class="item-current item-' . get_the_time('j') . '"><h2><strong class="bread-current bread-' . get_the_time('j') . '"> ' . get_the_time('jS') . ' ' . get_the_time('M') . ' Archives</strong></h2></li>';

        } else if (is_month()) {

            // Month Archive

            // Year link
            echo '<li class="item-year item-year-' . get_the_time('Y') . '"><a class="bread-year bread-year-' . get_the_time('Y') . '" href="' . get_year_link(get_the_time('Y')) . '" title="' . get_the_time('Y') . '">' . get_the_time('Y') . ' Archives</a></li>';
            // echo '<li class="separator separator-' . get_the_time('Y') . '"> ' . $separator . ' </li>';

            // Month display
            echo '<li class="item-month item-month-' . get_the_time('m') . '"><h2><strong class="bread-month bread-month-' . get_the_time('m') . '" title="' . get_the_time('M') . '">' . get_the_time('M') . ' Archives</strong></h2></li>';

        } else if (is_year()) {

            // Display year archive
            echo '<li class="item-current item-current-' . get_the_time('Y') . '"><h2><strong class="bread-current bread-current-' . get_the_time('Y') . '" title="' . get_the_time('Y') . '">' . get_the_time('Y') . ' Archives</strong></h2></li>';

        } else if (is_author()) {

            // Auhor archive

            // Get the author information
            global $author;
            $userdata = get_userdata($author);

            // Display author name
            echo '<li class="item-current item-current-' . $userdata->user_nicename . '"><h2><strong class="bread-current bread-current-' . $userdata->user_nicename . '" title="' . $userdata->display_name . '">' . 'Author: ' . $userdata->display_name . '</strong></h2></li>';

        } else if (get_query_var('paged')) {

            // Paginated archives
            echo '<li class="item-current item-current-' . get_query_var('paged') . '"><h2><strong class="bread-current bread-current-' . get_query_var('paged') . '" title="Page ' . get_query_var('paged') . '">' . __('Page') . ' ' . get_query_var('paged') . '</strong></h2></li>';

        } else if (is_search()) {

            // Search results page
            echo '<li class="item-current "><h2><strong class="bread-current " title="Search results for: ' . get_search_query() . '">Search results for: ' . get_search_query() . '</strong></h2></li>';

        } elseif (is_404()) {

            // 404 page
            echo '<li>' . 'Error 404' . '</li>';
        }

        echo '</ul>';
        echo '</div>';
    }

    $breadcrums = ob_get_clean();
    return $breadcrums;

}

add_shortcode('sv-breadcrumbs', 'sv_custom_breadcrumbs');

// End SV Breadcrumbs
.custom_breadcrumbs {
    list-style: none;
    padding: 0;
    margin: 0;
}

.custom_breadcrumbs li {
    display: inline-block;
}

.custom_breadcrumbs li a {
    text-decoration: none;
    color: #333;
    padding: 5px 10px;
}

.custom_breadcrumbs li a:hover {
    color: #000;
}

.custom_breadcrumbs li:after {
    content: ">";
    padding: 0 5px;
    color: #ccc;
}

.custom_breadcrumbs li:last-child:after {
    content: "";
}

Now that you’ve added the custom code, you can display the breadcrumbs on your website. There are two methods to achieve this:

If you want to display breadcrumbs within your posts or pages, use the [sv-breadcrumbs] shortcode. Simply insert this shortcode where you want the breadcrumb trail to appear.

For more flexibility, you can directly call the sv_custom_breadcrumbs() function in your theme files. For instance, add the following code snippet to your template files (e.g., header.php, single.php, etc.):

echo sv_custom_breadcrumbs();

By following these steps, you can create custom breadcrumbs tailored to your website’s structure. Remember that well-implemented breadcrumbs not only improve user experience but also enhance your site’s SEO. Happy coding!


Feel free to customize the code further based on your specific requirements. If you have any questions or need additional assistance, feel free to ask! 😊

Related posts

How to Submit and Upload a New WordPress Plugin to SVN | Complete Guide

The Best Programming Languages for Web Development in 2024

Effortlessly Duplicate Pages, Posts, and Products in WordPress Without Plugins