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:
WordPress change front page option

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:

  1. If a static Page is used as front page, put that Page first on the link and make sure wp_list_pages does not show that Page’s link again.
  2. 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_menu can 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, use wp_list_pages.
  • wp_page_menu can only give a class attribute to its <ul>, not ID. A small gotcha that might require you to rewrite your CSS if you used wp_list_pages with ID before.
  • wp_list_pages allows you to set the depth parameter to control the level of hierarchy you want to show. This parameter isn’t available in wp_page_menu, so the alternative will be to exclude the Pages’ ID one by one. Update: Actually possible with wp_page_menu.
  • By default, wp_list_pages outputs “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?

You may also like...

12 Responses

  1. Nathan Rice says:

    Or, you can just use wp_page_menu(), which does all this for you.

    It has some limitations, but generally, it makes things easier.

  2. Hafiz Rahman says:

    Woah, awesome function, there. I’ve updated the post to add your suggestions. Thanks a bunch, Nathan!

  3. Nathan Rice says:

    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!

  4. Hafiz Rahman says:

    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

  5. Ronny says:

    A much prettier way to write this line:

    <li class=”current_page_item”>

    Is the following:

    <li class="”>

    Easier, shorter and more readable :)

  6. Ronny says:

    The PHP disappeared, sorry.

    You can find the code snippet here: http://snipplr.com/view/29248/short-echo/

  7. Mkyrk says:

    hi…

  8. Rick says:

    I’m trying to use session variables and cookies (my first attempt with these), to hide one page on the menu until website terms are accepted and submitted via a form. I have a working function to hide content if it remains on the form page, but I need that content to be on a separate page and that page should only appear in the menu if the conditions are met.

    I’m new to wp_page_menu and more than a little confused as to how to modify the pages that it displays. Hoping the solution is simple enough that someone here can help.

  9. Not to take away from the wp_page_menu option, but if you were sticking with your original solution, couldn’t you just do this :

    <a href="/">Home

    I’ve left the class stuff out for simplification, but we’re simply saying if it’s not set to show a static page, show the home link. If it is using a static page then the home link will be left out. To get the correct page first in the menu, simply tweak the Page Order setting. I haven’t tried it, but wouldn’t that work?

  10. Okay, wrapping my code in code tags didn’t work – lets try this:

    <?php if ( ‘page’ != get_option( ‘show_on_front’ ) ) { ?>
    <li><a href="<?php echo get_option( ‘home’ ); ?>/">Home</a></li>
    <?php } ?>

  11. Hafiz Rahman says:

    Stephen: Yes that seems to be another neat idea as well.

    By the way to everyone reading, WP 3’s menu nav will be the standard way for doing things from now on, instead of how it is done in this tutorial.

  1. June 21, 2010

    […] are other ways of dealing with this, but my way seems simplest to me. Note: To get the static home page to appear first, the user can […]

css.php