# 1210

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?

7 Responses to “Bulletproof WordPress Pages-based Navigation”

  1. Nathan Rice

    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

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

  3. Nathan Rice

    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

    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

    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

    The PHP disappeared, sorry.

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

Leave a Reply