DiggingIntoWordPress

by Chris Coyier & Jeff Starr

Delicious Recipes for WordPress Page Menus and Page Listings

Posted by on

There are so many awesome ways to display your WordPress pages. Out of the box, WordPress provides two different template tags for displaying lists of your site’s pages. The first, most-commonly used tag is wp_list_pages(), and the second, lesser-known tag is wp_page_menu(). First we’ll explore the highly flexible wp_list_pages() template tag, and then we’ll dig into the new wp_page_menu() tag. Along the way, we’ll check out some delicious recipes, tips and tricks for creating the perfect WordPress Page Menu.

Digging into the wp_list_pages() template tag

Almost everyone is familiar with the wp_list_pages() template tag, and it has served us well since WordPress version 1.5. Using this tag is easy, just add the following code to your sidebar or other choice location:

<?php wp_list_pages(); ?>

And then you can customize that in many ways using the fine menu of arguments provided at its Codex Reference Page. Some of the highlights include:

Extreme Sorting Power

You can sort your pages by the descriptor of any field in the wp_post table of the WordPress database. By default, you get alphabetical by page title, but there are some other great options as well. Here are some delicious copy-&-paste recipes for your next project:

<?php wp_list_pages('sort_column=menu_order'); // sort by admin-specified order ?>
<?php wp_list_pages('sort_column=post_date'); // sort by time of page creation ?>
<?php wp_list_pages('sort_column=post_name'); // sort by name of the page slug ?>
<?php wp_list_pages('sort_column=id&sort_order=asc'); // page id in ascending order ?>
<?php wp_list_pages('sort_column=id&sort_order=desc'); // page id in descending order ?>

That first one there is a little wonky, but can prove very helpful for getting your pages to display in any order imaginable. To configure custom Page order to display via the menu_order parameter, you simply assign each Page a numerical value in its Write/Edit Admin screen. You can see a full list of available descriptors here.

Include and Exclude Anything

WordPress makes it easy to include and exclude any pages to create the perfect page menu. By default, the wp_list_pages displays all pages, but this is easily customized with three useful arguments:

<?php wp_list_pages('exclude=1,2,3'); // exclude only these three page ids ?>
<?php wp_list_pages('include=1,2,3'); // include only these three page ids ?>
<?php wp_list_pages('exclude_tree=1,2,3'); // exclude these parent pages and all children ?>

The exclude_tree parameter is new as of WordPress version 2.7. It makes it super-easy to omit an entire branch of pages (the parent page and all descendant pages) from your page listings.

Control the Depth of Pages

There are two parameters designed to control the depth of pages displayed by the wp_list_pages() tag. The first is the depth parameter, which by default displays all subpages, regardless of depth. There is also the child_of parameter, which defaults to a value of 0 to display all pages and subpages. Here are some recipes to customize these default values:

<?php wp_list_pages('depth=0'); // display all pages and subpages via indented lists ?>
<?php wp_list_pages('depth=-1'); // display all pages and subpages without indentation ?>
<?php wp_list_pages('depth=1'); // display only top-level pages ?>
<?php wp_list_pages('depth=2'); // display all pages and first level subpages ?>
<?php wp_list_pages('depth=n'); // display all pages and subpages to the nth level ?>

<?php wp_list_pages('child_of=0'); // displays all pages and subpages ?>
<?php wp_list_pages('child_of=3'); // displays all subpages of page with id 3 ?>

The child_of parameter is a great way to display sub-menus of specific pages, for example for implementation of drop-down menus and so forth. Note that multiple instances of wp_list_pages() may be used on any given web page.

Use with Custom Fields

Here is an extremely powerful way to customize your page menus using custom-field values. Using meta_key and meta_value parameters, you can tell WordPress to display only those pages that contain specific custom-field key/value pairs. Here are some examples:

<?php wp_list_pages('meta_key=menu&meta_value=page'); // display pages with menu/page custom field ?>
<?php wp_list_pages('meta_key=icon&meta_value=true'); // display pages with icon/true custom field ?>

This parameter also gives you the flexibility of modifying page listings without having to touch any source code. Great for clients and family members who may not feel comfortable “digging in” ;)

Even more functionality (new in WordPress 2.7 & 2.8)

WordPress recently rolled out some new tricks for the wp_list_pages() tag. Namely, we now have the ability to wrap the anchor text of our page listings with any HTML or text. Other new features include the number of pages to display as well the number of pages to pass over when displaying page listings. Let’s have a look:

<?php wp_list_pages('link_before=<span>&link_after=</span>'); // wrap anchor text with a span tag ?>
<?php wp_list_pages('number=7'); // display only the first seven pages ?>
<?php wp_list_pages('offset=7'); // skip the first seven pages ?>

A complete copy/paste recipe for wp_list_pages()

There are several more useful parameters for the wp_list_pages() tag, but rather than explaining each one, I will refer you to the Codex for all the boring details. So now, before digging into the juicy stuff, here is a “super-delicious” tag recipe equipped with every possible parameter, especially designed for easy customization:

<?php wp_list_pages('sort_column=post_title&sort_order=asc&exclude=0&exclude_tree=0&include=0&depth=0&child_of=0&show_date=&date_format=&title_li=&echo=1&meta_key=menu&meta_value=page&link_before=<span>&link_after=</span>&authors=0&number=0&offset=0'); // contains all parameters as of version 2.8 ?>

Alright, enough of the basics, let’s explore some truly delicious page-listing recipes.

List all subpages of current page

Here is a way to simplify your sidebars and overall page designs. Instead of listing every subpage on all of your site’s web pages, you can dynamically display subpages only when the visitor is viewing the parent page. So for example, your homepage sidebar would list all of your parent pages, and then each of the parent pages would display a list containing all of its subpages. Here’s one way to do it, as demonstrated in the Codex:

<?php 
$children = wp_list_pages('title_li=&child_of='.$post->ID.'&echo=0');
if ($children) { ?>
	<ul>
		<?php echo $children; ?>
	</ul>
<?php } ?>

Placed in your theme template file, this code will generate a list of all subpages for the current parent page. If there are no subpages, nothing will be displayed.

As is, this method will only display the subpages when visiting the parent page, but we can modify the code so that all subpages are displayed when visiting either the parent page or any of the subpages:

<?php
if($post->post_parent)
	$children = wp_list_pages("title_li=&child_of=".$post->post_parent."&echo=0");
else
	$children = wp_list_pages("title_li=&child_of=".$post->ID."&echo=0");
if ($children) { ?>
	<ul>
		<?php echo $children; ?>
	</ul>
<?php } ?>

And you can do even better than that. With this next method, your page listings will be displayed as follows:

  • When visiting main page, all top level pages are listed in the sidebar.
  • When visiting a top level page with no children, all top level pages are listed.
  • When visiting a top level page with children, just the children pages, and descendant pages, are listed.
  • When visiting a child page, just the children, and descendant pages, of that parent, are listed.
<?php
$output = wp_list_pages('echo=0&depth=1&title_li=<h2>Top Level Pages</h2>' );
if (is_page( )) {
	$page = $post->ID;
	if ($post->post_parent) {
		$page = $post->post_parent;
	}
	$children=wp_list_pages( 'echo=0&child_of=' . $page . '&title_li=' );
	if ($children) {
		$output = wp_list_pages ('echo=0&child_of=' . $page . '&title_li=<h2>Child Pages</h2>');
	}
}
echo $output;
?>

All this is great, but it’s also helpful to know how to style the markup generated by the wp_list_pages() template tag. Let’s look at that next..

Styling wp_list_pages() markup

Here is the default markup generated by the wp_list_pages() template tag (assuming a total of five pages with the current page having an ID of “1”):

<li class="pagenav">Pages
	<ul>
		<li class="page-item-1 page_item current_page_item">Page ID 1</li>
		<li class="page-item-2 page_item">Page ID 2</li>
		<li class="page-item-3 page_item">Page ID 3</li>
		<li class="page-item-4 page_item">Page ID 4</li>
		<li class="page-item-5 page_item">Page ID 5</li>
	</ul>
</li>

You can strip the outer <li class="pagenav"> and the enclosing <ul></ul> tags by including “title_li=” (empty string) as a parameter in the wp_list_pages() template tag. You may also keep these outer tags and customize the “Pages” text with whatever markup and/or text you need. Instead of an empty string, use something like “title_li=<h2>Pages</h2>” instead. Note also that specific class attributes will be applied to both parents and ancestors of the current page (see next section).

Styling page menus generated by wp_list_pages()

Here is a list of the selectors available for styling your page menus:

.pagenav {}               /* li tag containing page menu */
.page_item {}             /* inlcuded on every page item */
.page-item-n {}           /* specifies page with id of n */
.current_page_item {}     /* specifies the current page */
.current_page_parent {}   /* any parent of current page */
.current_page_ancestor {} /* any child of current page */

Now, ready for some new stuff? Brace yourself..

Digging into the wp_page_menu() template tag

So what about this new wp_page_menu() template tag? With all of the flexibility of wp_list_pages(), why is it necessary? In a nutshell, wp_page_menu() is a simplified version of wp_list_pages(). In addition to accepting a few of the same parameters, this new tag brings one new trick to the table: it provides the ability to add your site’s Home page to the list of Pages displayed. This tag was introduced in WordPress version 2.7, and, in my opinion, should have been integrated into the existing Page-listing template tag. In any case, for those looking for an easy way to include your Home page into the list of Pages, here you go:

<?php wp_page_menu(); ?>

As mentioned, most of the parameters are the same as before. Here is a list of common parameters:

  • sort_column
  • include
  • echo
  • link_before
  • link_after

And — drum roll please — here are the new parameters: menu_class and show_home! Let’s check ‘em out..

menu_class

This is a no-brainer. It simply specifies the class attribute for the surrounding <div>. Yes, the list is wrapped in a division. Any string may be used, here is an example:

<?php wp_page_menu('menu_class=pages'); // wrap list in div with class="pages" ?>

show_home

Ahh, the long-awaited “include-the-home-page” functionality. Now, instead of hacking your client’s functions.php file to include the Home Page in the main menu, you can simply do this instead:

<?php wp_page_menu('show_home=1'); // include the home page in the list ?>
<?php wp_page_menu('show_home=0'); // exclude the home page in the list ?>

By default, this parameter returns false and no Home Page is displayed. When set to true, the Home Page will be displayed as the first item in the page list. The URL for the Home Page is taken from the value for your site’s “Blog address”, as specified in the WordPress Admin under Settings > General.

When the Home Page is displayed, the default anchor text is simply “Home”, but you can customize that to whatever you want by specifying such for the parameter value:

<?php wp_page_menu('show_home=Digging%20into%20WordPress'); // include the home page ?>
<?php wp_page_menu(array('show_home'=>'Digging into WordPress', 'sort_column'=>'menu_order')); // include the home page ?>

For more information on this new tag, check it out at the WordPress Codex

Take-home message

WordPress provides two different ways to list your pages: wp_page_menu() and wp_list_pages(). The wp_list_pages() tag is powerful, flexible and fully functional. So, unless you need to include your Home Page, your best bet is always wp_list_pages(). For page menus that include the Home Page, you will need to use wp_page_menu() instead. Beyond listing your Home Page, the wp_page_menu() can do a few things, but nothing that wp_list_pages() can’t already handle.

My question for the day is: Why wasn’t the home-page functionality integrated into the existing tag? Why complicate things with an otherwise redundant tag? Share your thoughts!

50 Responses

  1. Daniel Groves July 13, 2009

    Jeff,

    Thanks for being a real legend here. I am currently working on a project and have been trying to find a few of the different syntax’s that you have given examples for here.

    Thanks,
    Dan.

  2. You wouldn’t believe the hours I spent researching and learning this stuff. Where were you when I needed you? ;-)

    Just to point out to anybody who might be confused, if you are using WP as a CMS and using a WP page for your homepage (and not index.php) then wp_list_pages() will list “Home”.

  3. Thanks a lot for this article, I just did a client’s project where I manually coded in the menu with IF statements to check if each page was the current one to apply the correct class. This is a much cleaner solution!

  4. I guess that not including home in wp_list_pages is because it isn’t a page in that sense. :)

    wp_page_menu isn’t simplified version. It is wrapper that turns list of pages into full navigation with wrapping div. I think it also adds class for current page so it can be styled differently.

    As many things in WP code difference seems to be in semantics – list of pages for whatever or navigation menu. Similar in content and code but different in purpose.

    • wp_list_pages also adds current page classes, works on child pages too.

  5. Something unsual happened in my feed reader here. None of the lines wrap. I wouldn’t expect the code blocks to wrap but all the text gives me giant horizontal scrolling. I’m not sure if it’s just my reader or if you have a feed problem but I just thought I’d give a heads up. Thanks for the article.

    • Jeff Starr

      Not sure what could be happening there, Ryan. I checked our feed and everything looked good. Let us know if you trace the issue back to our feed.

  6. wp_list_pages() has prity much done everything ive ever needed it to do…

    the topic of “this otherwise redundant tag” makes me think that the wp developers have been trying to make the menu system work a lot easier, as a lot more users move to full CMS usage, trying to make it easier for beginners/people who are just “diggin in ;)”

    but for me, wp_list_pages will do the job just fine..
    good article cheers : stumbled

  7. Hi,

    I was struggling earlier (sorted now) with adding the static pages link to the sidebar. My theme is not a widget ready one so nowhere to get the ‘pages’ from for the sidebar.

    I then saw the asides. This works with your link I think? I was thinking on ‘Worth a Visit’.

  8. wp_page_menu() isn’t really redundant. It’s specifically for creating a page menu as opposed to just any ol’ page list. It’s a wrapper for the wp_list_pages() function that adds elements commonly-added to themes such as the containing &lt;div> and the “Home” link. The home link doesn’t necessarily fit in with wp_list_pages() since it’s technically not a page. Of course, that ability could’ve been added.

    wp_page_menu() also introduces the wp_page_menu_args and wp_page_menu filter hooks. The former is much better at allowing plugins to overwrite your page menu, which is something not as easily done with wp_list_pages().

    One could also argue that a filter hook for the arguments could have been added to wp_list_pages() too, but there might have been the need for a workaround on wp_list_pages_excludes for that to work right.

    When creating a menu, wp_page_menu() is the correct function to use when creating a theme for public release because it’s easy for plugins to filter the arguments or even users from their functions.php file.

    • Jeff Starr

      I didn’t see the wp_page_menu_args and wp_page_menu filter hooks mentioned/explained in the Codex, nor did I see anything about wp_list_pages_excludes. I assume the latter enables us to filter which pages are excluded using wp_list_pages()? Would be great to get some of this newer stuff documented.

      You have a point about using wp_page_menu() for publicly-released themes, but when it comes to customizing page menus/lists, wp_list_pages() is preferable because of its superior flexibility. The ability to filter a few variables seems less convenient than the ability to create highly customizable and complex page-navigation systems.

  9. Jeff, thanks for this extensive writeup. I remember wp_page_menu() being introduced, but I never saw a proper explanation of how it really differed from wp_list_pages. Now I am more than enlightened!

    Also, thanks to Justin Tadlock for clarifying that wp_page_menu() is filter-able. Good to know.

  10. Darfuria July 13, 2009

    Any tips for applying a class to the last item generated by wp_list_pages? For example, say you have a border-right on all of the list items, but don’t want one on the last list item.

    Obviously if you know that page-item-5 is always going to be the last item in the list, then that’s easy, but if it could change..

  11. MorayWeb July 14, 2009

    Woohoo! What an article – I too have spent many ours digging around for this kind of info presented in an understandable and readable way, brilliant! :)

  12. Great stuff! This will really help me to get more control over my menus. Thanks.

  13. Awesome post! This info will definitely come in handy. It’s nice to see all the options explained in one post.

  14. Sebastian Cruz July 14, 2009

    Hi!! very useful recipe, any way to put images to the subpage listing?? thanxs!

  15. Quick fact I learned today:

    If you publish a Page and set WordPress to use that as the static homepage, it won’t exclude it by passing show_home=0 to wp_page_menu, you’ll have to exclude that normally through the exclude=xxx param.

    • Jeff Starr

      Yes, that seems to make sense. I think the wp_page_menu() is using the value of the blog’s home page as specified in the Admin area, as opposed to any created page. Good to know.

  16. I want list current page and its sub pages. Now it displays only sub pages not parent pages of current page(level 1 page). I appreciate your help.

  17. Darfuria July 20, 2009

    Is there a way of listing parent pages, or offsetting the depth of list_pages by 1?

    I have my top-level navigation horizontally across the top of the page, and then the second and third level vertically in the sidebar. When I am on a third level page, it’d be nice if I could display the second level pages, and their parents.

    • Try this code, I have it in the sidebar and it works as I need it to:

      <?php
      if(!$post->post_parent) {
             // will display the subpages of this top level page
             $children = wp_list_pages("title_li=&child_of=".$post->ID."&echo=0");
      }
      else {
             // displays only the subpages of parent level
             // $children = wp_list_pages("title_li=&child_of=".$post->post_parent."&echo=0");
             if($post->ancestors) {
                    // now you can get the the top ID of this page
                    // wp is putting the ids DESC, thats why the top level ID is the last one
                    $ancestors = end($post->ancestors);
                    $children = wp_list_pages("title_li=&child_of=".$ancestors."&echo=0");
                    // you will always get the whole subpages list
             }
      }
      if ($children) { ?>
             <ul>
                    <?php echo $children; ?>
             </ul>
      <?php } ?>

      If a top-level page has subpages, and if you are on such a top-level page, it displays full list of subpages for that top level page.

      If you are on a subpage of such a top-level page, you still can see all subpages for that page (in levels both down and up).

  18. This is very good documentation than codex!

    Btw, how if I want to list specific page with all sub pages ?

    Using this syntax

    Not showing the sub pages..

  19. Thanks for the great code above to only show only the pages I want using the include function.

    But I am curious as to how I can remove the “Page” text that appears as a parent to the pages?
    Any help would be appreciated!

  20. Amazingly detailed code examples! Thank you very much for this! Totally got me unstuck!

  21. i’ve tried all of these examples and none of them seem work the way they should, or at least the way i think they should from their descriptions. it would be nice to see working examples of these.

    for myself, what i am trying to achieve is this:

    i have my top tier pages listed horizontally across the top.

    when i navigate to one of the top tier pages, i want to see it’s children in my sidebar navigation, but NOT their children:

    Tier 1 page (current page)
    – Tier 2 Page 1
    – Tier 2 Page 2
    – Tier 2 Page 3
    – Tier 2 Page 4

    when i click on a 2nd tier page, my sidenav still shows the 2nd tier pages but has expanded the current 2nd tier page to show it’s children:

    Tier 1 Page
    – Tier 2 Page 1
    – Tier 2 Page 2 (current page)
    – Tier 3 Page 1
    – Tier 3 Page 2
    – Tier 3 Page 3
    – Tier 2 Page 3
    – Tier 2 Page 4

    this would also be maintained when going to a 3rd tier page:

    Tier 1 Page
    – Tier 2 Page 1
    – Tier 2 Page 2
    – Tier 3 Page 1 (current page)
    – Tier 3 Page 2
    – Tier 3 Page 3
    – Tier 2 Page 3
    – Tier 2 Page 4

    i think this is pretty standard navigation for a web site, but i cannot find code that will do this. does anyone have this? and a link to a site where it is working?

    thanks

    • oops, those side nav examples didn’t indent properly for the tier 3 pages, but i think you get the idea…

      • Darfuria August 18, 2009

        I don’t have the codex to reference right now, but I imagine you’d write an if is child statement and specify the depth of pages you want to list.

        I found this nifty bit of code to list the root page’s pages, so maybe you can fiddle with it:

        function get_root_parent($page_id) {
             global $wpdb;
             $parent = $wpdb->get_var("
                  SELECT post_parent
                  FROM $wpdb->posts
                  WHERE post_type='page'
                  AND ID = '$page_id'");
             if ($parent == 0) return $page_id;
             else return get_root_parent($parent);
        }

  22. Tiziano August 25, 2009

    I have a question. what about adding extra text under each navigation item. I don’t think I read this on the page… otherwise my bad!

    for example, can you add a custom field to a page with some text as value and call it in wp_list_pages() so it outputs your navigation item + the value of the custom field

    I was thinking at this code, but don’t know how to call the value.

    <?php wp_list_pages('link_after=<br /><span class="txt">value here</span>'); ?>

  23. What script is needed to simply indent the children pages under the Menu Items?
    Thanks.

  24. Edward,
    Did you ever find a solution to this question? I have spent about 40 hours trying and still have had no luck. It seems like such a vasic thing. I wouldn’t even mind if the parent pages were bold and the sub pages not. I just want them to look different.
    Nicole

    • Nicole, that’s 40 hours you could have spent learning another CMS that has all this built-in!

      I love WordPress, but navigation is truly a weak spot.

      I’m not a PHP person but all you would need to do is apply a class with text-indent specified to the children right? Now, a plugin to do all this stuff would really go a long way in helping WordPress become a true CMS.

      • You know how it is with these things. I have been spending “just a few more minutes” for days….
        I tried Drupal and Joomla but they are soooo complicated…
        I don’t understand how nobody has come up with this?? Which CMS do you recommend?

      • Well I love WordPress, don’t get me wrong. I have a Joomla site and hated it as well, will never make that mistake again, although in it it’s easy to do split menus, everything else about it sucks. Drupal seems just too much for me as well. I just created my first template in ModX and REALLY liked it. Granted you will have to spend time to get to know it. And it’s definitely not as elegantly simple as WordPress in handling comments and RSS feeds, nor are there as many plugins for it. But its navigation component called Wayfinder is very flexible and can do all this stuff above. And it’s not PHP, it has its own tagging syntax that’s pretty straightforward. So I would recommend it as a possibility.

        Other people talk about Expression Engine a lot but I haven’t tried it so I can’t say. I think a lot of that is just EE’s guerilla marketing, they do a lot of blog commenting I’ve noticed.

  25. Hey Nicole. Sorry about the 40 hours you spent looking for stuff for me. Actually it was pretty simple. Just go into the Sidebar PHP and insert simple indent HTML script in it to indent children pages in WordPress.

  26. I tried Joomla and Drupal too Nicole. In the end I settled for WordPress. Most user friendly application I found so far. Forum is a lot of help too.

  27. jonnypage September 3, 2009

    any ideas on how to add a class to each specific level of page navigation?

    • Jeff Starr

      Perhaps something like this would work:

      <?php wp_list_pages('link_before=<span class="whatever">&link_after=</span>'); // wrap anchor text with a span tag ?>

      See the post for more details on this.

      • jonnypage September 23, 2009

        that would add the same class to every level of navigation. Not each specific level. Ie:

        parent: class: parent
        child: class: child
        child-child: class:child-child

        etc..

      • Jeff Starr

        Ah, gotcha. There should be an easy way to do this with WordPress.. I will keep my eyes open for a solution.

  28. Robert Reynolds September 12, 2009

    Another great article about using WordPress as a CMS.

    In regards to WordPress Pages functions, I would like to have a horizontal menu across the top. The Parent pages would be listed inline, but when hovering over the parent, a dropdown of the corresponding children pages would show.

    I have used a theme that does this well in Firefox using Javascript, but does not work in IE. Iwould like the horizontal menu to be pure css if possible.

    Anyone have suggestions for this task?

  29. FYI, the ‘number’ argument for wp_list_pages does not work properly. If you look at the code for wp_list_pages here, you won’t see the “number” argument. Seems like the code does not work as advertised, sadly.

    http://core.trac.wordpress.org/browser/trunk/wp-includes/post-template.php

  30. Thanks! this is a valuable resource and a great addition to the codex.

    I’m stuck on something that I can’t figure out though – I’m sure it’s simple and it’s evading me.

    I’m using wp_list_pages to build a drop down menu;

    My parent page is “bikes” and below that I have sub pages, bike1, bike2, bike3, etc.

    I’m using css to style and build the drop downs. My problem is that the top level page is never rendered as a link on my menu – just a “label”

    How do I tell wp_list_pages to make that parent page a link as well?

    Thanks in advance.

Comments are closed. Contact us with any critical information. Thank you!

Code is poetry