Bulletproof WordPress Pages-based Navigation
One of the most used navigation method used in WordPress themes is to list all of the blog’s Pages and put it horizontally on the header area. The code will look like this:
<ul id="nav"> <li <?php if ( is_home() ) { ?> class="current_page_item"<?php } ?>> <a href="<?php echo get_option( 'home' ); ?>/">Home</a> </li> <?php wp_list_pages( 'title_li=' ); ?> </ul>
The most important piece there is the wp_list_pages function, which will automatically list all Pages and add class="current_page_item" to the Page link if it’s currently being opened.
Before that function, we add a little more code to display a front page link. Naturally we put it first on the list, and we add our own class for when the front page is currently open. All is well so far, and that is basically the code I’ve been using (including on Pico).
A Little Gotcha
And then there’s this:

Yes. WordPress allows us to pick a Page and use it as the front page via Settings > Reading. This means that in practice, the actual front page might differ from what’s in get_option('home') and if we keep using our old code, the front page link will be shown by wp_list_pages, not first on the list. Hardly ideal now.
So, to bulletproof our Page-based navigation, the following needs to be satisfied:
- If a static Page is used as front page, put that Page first on the link and make sure
wp_list_pagesdoes not show that Page’s link again. - If no static Page is being used as front page, though, then our old code should suffice.
The New Page-based Navigation Code
This is the code I come up with:
<ul id="nav"> <?php // Checks if a Page is being used as front page if('page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' )) { $front_id = get_option( 'page_on_front' ); $front = get_post( $front_id ); ?> <li <?php if ( is_front_page() ) { ?> class="current_page_item"<?php } ?>><a href="<?php echo get_permalink( $front_id ) ?>"><?php echo $front->post_title; ?></a></li> <? wp_list_pages( 'title_li=&exclude='.$front_id ); } // No static Page as home page else { ?> <li <?php if ( is_front_page() ) { ?> class="current_page_item"<?php } ?>><a href="<?php echo get_option( 'home' ); ?>/">Home</a></li> <?php wp_list_pages( 'title_li=' ); } ?> </ul>
If a Page is used as a front page, then we show it first, use its title as the link text (naturally), and then show the rest of the Pages excluding it. Else we show the navigation the old way. Additionally, I also use is_front_page for both cases to maintain code consistency.
Update: A Better Alternative
Nathan Rice pointed out in the comments area about a better function for this: wp_page_menu. With this function, we can get our expected menu by doing something like this:
wp_page_menu( 'menu_class=nav&show_home=1' );
And that’s it! All in one pleasant line of code. A few differences (that are hardly critical):
wp_page_menucan only output menu in the format of an unordered list (with <ul>). Not a big deal because this is the commonly used HTML structure and it works with various dropdown techniques. If you want your to make your own structure, usewp_list_pages.wp_page_menucan only give a class attribute to its <ul>, not ID. A small gotcha that might require you to rewrite your CSS if you usedwp_list_pageswith ID before.wp_list_pagesallows you to set the depth parameter to control the level of hierarchy you want to show. This parameter isn’t available inwp_page_menu, so the alternative will be to exclude the Pages’ ID one by one. Update: Actually possible withwp_page_menu.- By default,
wp_list_pagesoutputs “Home” as the link text for the front page. You can change this to anything you want by setting the parameter ’show_home=your_text_here’, but you can’t make it automatically use the Page’s title as the link text.
As Nathan mentioned, wp_page_menu is generally enough for most use cases. I agree and will use it over my own code before. So thanks, Nathan!
A Little Help, Please?
This is as best I can come up with. Play around with it, break it, and help find tricky cases not covered by the code. Also, there are probably many ways to optimize the code above. Do let me know in the comments, alright?
Or, you can just use wp_page_menu(), which does all this for you.
It has some limitations, but generally, it makes things easier.
Woah, awesome function, there. I’ve updated the post to add your suggestions. Thanks a bunch, Nathan!
Try passing the depth parameter to wp_page_menu … even though it’s not listed as an argument on the Codex, wp_page_menu actually calls wp_list_pages and passes the parameters you use in wp_page_menu to the wp_list_pages function, so depth should (theoretically) work.
Good stuff!
Just tested it and it did work. Thanks, have updated the post again.
To those curious about the inner workings of wp_page_menu, it’s in wp-includes/post-template.php
A much prettier way to write this line:
<li class=”current_page_item”>
Is the following:
<li class="”>
Easier, shorter and more readable :)
The PHP disappeared, sorry.
You can find the code snippet here: http://snipplr.com/view/29248/short-echo/
hi…