In this tutorial I am going to show you how to do proper, full breadcrumbs in Wordpress including nested categories and nested pages. Breadcrumbs are a pretty standard design pattern and can be very useful in a lot of website situations.
![]()
I’m aware there are already some good plugins for adding breadcrumbs in Wordpress but I’m going to show you how to do it the good old fashioned way.
The PHP Code
Since I like to copy and paste I’ll post the full code first and then take a look at what it’s doing. You can just include this function in your theme’s “functions.php” file.
function get_breadcrumbs() { global $wp_query; if ( !is_home() ){ // Start the UL echo '<ul class="breadcrumbs">'; // Add the Home link echo '<li><a href="'. get_settings('home') .'">'. get_bloginfo('name') .'</a></li>'; if ( is_category() ) { $catTitle = single_cat_title( "", false ); $cat = get_cat_ID( $catTitle ); echo "<li> » ". get_category_parents( $cat, TRUE, " » " ) ."</li>"; } elseif ( is_archive() && !is_category() ) { echo "<li> » Archives</li>"; } elseif ( is_search() ) { echo "<li> » Search Results</li>"; } elseif ( is_404() ) { echo "<li> » 404 Not Found</li>"; } elseif ( is_single() ) { $category = get_the_category(); $category_id = get_cat_ID( $category[0]->cat_name ); echo '<li> » '. get_category_parents( $category_id, TRUE, " » " ); echo the_title('','', FALSE) ."</li>"; } elseif ( is_page() ) { $post = $wp_query->get_queried_object(); if ( $post->post_parent == 0 ){ echo "<li> » ".the_title('','', FALSE)."</li>"; } else { $title = the_title('','', FALSE); $ancestors = array_reverse( get_post_ancestors( $post->ID ) ); array_push($ancestors, $post->ID); foreach ( $ancestors as $ancestor ){ if( $ancestor != end($ancestors) ){ echo '<li> » <a href="'. get_permalink($ancestor) .'">'. strip_tags( apply_filters( 'single_post_title', get_the_title( $ancestor ) ) ) .'</a></li>'; } else { echo '<li> » '. strip_tags( apply_filters( 'single_post_title', get_the_title( $ancestor ) ) ) .'</li>'; } } } } // End the UL echo "</ul>"; } }
We then include the following code on any pages you want breadcrumbs to appear:
<?php get_breadcrumbs(); ?>
The CSS Code
The CSS code is just adding some simple styling so that the links appear inline. This can be styled as you wish.
ul.breadcrumbs { list-style: none; padding: 0; margin: 0; font-size:12px; } ul.breadcrumbs li { float: left; margin: 0 5px 0 0; padding: 0; }
The Explanation
So what are we doing here? Well lets start at the start.
- First off don’t show breadcrumbs if it’s the homepage. If it’s not we add our first link which is to the top level homepage.
- We then have a simple set of
elseifstatements which cover us for all the different situations we might find ourselves in. Some are fairly self explanatory like the seach, 404 and archive sections. - If it is a category we simply find the category ID then get list the parents of the current category. Wordpress helps us with this by providing a “get_category_parents()” function.
- If we are on a single page it gets a bit more in-depth. We have to find the category ID but in a slightly different way to above. However once you get the category ID it’s just the same method as above plus the addition of the title of the page you are on.
Now pages are slightly more in depth but nothing too complex:
- First off get the page title. Easy.
- We then find the ancestors of the page and reverse the order of them. Thankfully Wordpress has a get_post_ancestors() function making our life much easier. The next line just adds the current post to the array of ancestors.
- Finally we use a “foreach” loop to cycle through all of the ancestors and output them. The if statement in the loop makes sure that the last ancestor (the current page) doesn’t have a link.
So there you have it. Fully customizable, valid breadcrumbs in Wordpress. Please let me know what you think.
Download SourceUpdate 9th October
Thanks to mediadot for finding a small bug that left an un-closed <li> tag in the list. The code has been changed to remove this bug.
Update 26th October
Thanks to Uri Grinwald for finding a bug where li tags were in the wrong order on page breadcrumbs. The code has been changed to remove this bug.
Update 11th January
Thanks to antmeeks for pointing out some missing code in the article. I’ve also updated the description of how the pages are made as the get_post_ancestors() function made it far easier than before.






Very sweet! However, oddly I just used this and line 19 returned a parsing error, for the line including:
echo ” “;
However, having removed the ul class and thrown the whole thing in a div classed as breadcrumbs, the effect seems the same, minus the parsing error. Hope it was just me?
Silly me, in this code it’s line 8 – on MY code it was line 19.
Code ettiquette fail.
As Jan says, the correct code for line 8 would be:
echo ‘<ul class=”breadcrumbs”>’;
Thanks, Gilbert, for this code: very useful!
Actually, the best way to rewrite line 8 would be to add backslashes to the html, as such:
echo ” “;
The reason you use backslashes is because the backslash character (\) can be used to escape quotes and variables. The backslash allows PHP to essentially bypass the double quotation marks inside the HTML tag instead of crashing.
Oops, forgot to use entities!
echo ‘<ul class=\”breadcrumbs\”>’;
Dang it. One more time.
echo ‘<ul class=\“breadcrumbs\”>’;
Feel free to correct the first comment with the correct code and delete these two follow ups!
Thanks guys for the heads up. I’ve updated the code now so it should be fixed. Enjoy.
Thanks for this code. This is a great homegrown alternative to the handful of breadcrumb plugins available for WP.
You have some great WP oriented content on your site. I look forward to discovering more useful articles. Thanks again…
Hi Stevie. I’m glad you like this post and thanks for the complement.
My pleasure! Thinking about taking your WP CMS plugin for a spin…
This function is awesome…
I don’t need any breadcrumb plugin anymore. I hope this function will still be usable in the future wordpress…
Excellent function, thank you. But there is a little error – current post has opened that never close. Any ideas how to fix it?
Has an opened _li_
Thanks for pointing that one out. Updated the post.
Thanks for your reply, but now if (is_single()) – the last parent category lost their open _li_ ))
o_O
Ok changed it back. I thought you ment the if(is_single()) part was the one with the unclosed li. Which part do you mean?
Remove opening LI in line 36 helps to solve this problem. Thanks again for nice function
Thanks mediadot. I’ve updated the post and code to reflect the changes.
Easily add breadcrumbs to Wordpress – works like a charm http://bit.ly/2T8Icn
This comment was originally posted on Twitter
Hi,
Thank you for making this code available but I was wondering if you could help troubleshoot a bug I found. My site consist mostly of pages and sub pages and when I insert the code this is the result of the output html breadcrumb.
ul class=”breadcrumbs”> Dixon Hall » Our Services » Neighbourhood Programs
As you can see the home page as an extra closing and the current page has no closing
Thanks in advance.
Sorry here is the code using plain html entities
ul class="breadcrumbs"> <li><a href="http://ebizwindows.dynamic.ca">Dixon Hall</a></li></li><li> » <a href="http://ebizwindows.dynamic.ca/?page_id=5">Our Services</a></li><li> » Neighbourhood Programs</ul>
Hi Uri. Thanks for pointing that out. I’ve updated the code so that it is fixed. You can re-download the source code if you want to get the fixed code.
Updated my “How To: Breadcrumbs in Wordpress” article –> http://bit.ly/OKgWO
This comment was originally posted on Twitter
Thank you kindly will give it a re-try
The breadcrumb generates the page trail & titles with no issues… However, it doesn’t show the title of posts, nor the post’s parent categories… I’m using on WP 2.9.1 -
Hi antmeeks. Thanks for the heads up. I’ll have to check the compatibility for Wordpress v2.9+.
I tried it again this morning by downloading the source and it worked. Originally I had just copied the function from your article… So obviously there is a difference between the two versions because now it works fine. Perhaps you should eliminate the source in the body of your article?
Oh and BTW, nice work on this function! It’s absolutely perfect in it’s functionality and provides a very elegant, clean solution to a problem where a plugin would be overkill.
I will certainly be following your site now – Thanks, Ant
@antmeeks Thanks for pointing that out. I’ve updated the article and the code. Appreciate the comments.