Creating a Submenu in WordPress

For a project I’m currently working on I wanted to create a submenu that included the parent page as well as the supbages. I just wanted to display the submenu only if the parent page had subpages. Searching the WordPress Codex and googling for a solution I couldn’t quite find an example that took all these factors into consideration so I had to figure it out myself. Here’s the approach I came up with.

Checking if the page has subpages

I searched in vain for a method to determine if the current page has any subpages or not. I first assumed that there would be a has_subpages() method, but so far I haven’t found any. Lacking that, I came up with a very crude way of checking it. I explicitly had to try to fetch all the subpages with the function wp_list_pages() and then check if it returned anything. It’s not pretty but it works.

$children = wp_list_pages('&child_of='.$post->ID.'&echo=0');
if($children) {
    // This page has subpages
}

Checking if it’s a parent page or a subpage

The next thing I had to figure out was how to check i the current page is a parent page or a subpage. That’s done with the following code.

if(is_page() && $post->post_parent) { 
    // This is a subpage
} else {
    // This a parent page
}

Fetching the submenu

Now I needed a way to fetch the subpages. This is done with the wp_list_pages() function. The tricky part about this is that there’s no way to get both the parent page and the subpages in the same call. So therefor we have to call the function twice. The call also looks a little different depending on if we’re on the parent page or on the subpage.

if(is_page() && $post->post_parent) {
    // This is a subpage
    $children = wp_list_pages("title_li=&include=".$post->post_parent ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->post_parent ."&echo=0");
} else if($has_subpages) {
    // This is a parent page that have subpages
    $children = wp_list_pages("title_li=&include=".$post->ID ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->ID ."&echo=0");
}

There are other ways of doing this, but the benefit of this approach is that we automatically get class="current_page_item" on the list-item that represents the page that we’re currently on. That’s handy if you want to style that item in any particular way.

Outputting the HTML

The last step is to output the actual HTML. I’ve chosen to output it as an unordered list.

<?php // Check to see if we have anything to output ?>
<?php if ($children) { ?>
<ul class="submenu">
    <?php echo $children; ?>
</ul>
<?php } ?>

Putting it all together

Now it’s time to put all the pieces together. Just put this code in your page template one of the pages in your template, like for example page.php or sidebar.php and you’re good to go. These pages are located in /wp-content/themes/your-theme/.

<?php
$has_subpages = false;
// Check to see if the current page has any subpages
$children = wp_list_pages('&child_of='.$post->ID.'&echo=0');
if($children) {
    $has_subpages = true;
}
// Reseting $children
$children = "";

// Fetching the right thing depending on if we're on a subpage or on a parent page (that has subpages)
if(is_page() && $post->post_parent) {
    // This is a subpage
    $children = wp_list_pages("title_li=&include=".$post->post_parent ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->post_parent ."&echo=0");
} else if($has_subpages) {
    // This is a parent page that have subpages
    $children = wp_list_pages("title_li=&include=".$post->ID ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->ID ."&echo=0");
}
?>
<?php // Check to see if we have anything to output ?>
<?php if ($children) { ?>
<ul class="submenu">
    <?php echo $children; ?>
</ul>
<?php } ?>

Conclusion

I think that WordPress is an absolutely awesome CMS/Blog engine, but it do lack some handy methods. Fortunately it’s almost always possible to create workarounds. I hope that you will find this useful in your own WordPress Template. Don’t hesitate to tell me if you have a smarter way of doing this.

About the author

Gabriel Svennerberg is an UX Designer and FrontEnd Developer living in Sweden. When he's not busy designing or building applications at Meetod, he writes about UX Design and all things web on In usability we trust. Gabriel is the author of the book Beginning Google Maps API 3 published by Apress. Gabriel on Google+
  • Matt

    You mention adding this to the template file, where in the template file do you add this? and by template file i presume you mean “template.php” in admin>includes?

  • http://www.svennerberg.com Gabriel Svennerberg

    Hi Matt,

    No I mean one of the files that makes up a template, like for example page.php or sidebar.php in /wp-content/themes/your-theme/. Sorry for being unclear about that. I’ve updated the article accordingly.

  • http://www.mohr.com.au Tim

    Hey,

    I think this is missing a <?php opening statement. And even when I add that it doesn’t appear to work.

  • http://www.svennerberg.com Gabriel Svennerberg

    Tim: Your’e absolutely right Tim! There was a missing php opening statement. On top of that there was also a missing php closing statement. I’m really sorry about that. I have now corrected the code in the example so please try it again.

  • Dukessa
  • http://www.svennerberg.com Gabriel Svennerberg

    Dukessa: That’s sweet! If you’re not into building your own templates, that’s an excellent option. Thanks for the tip!

  • morsefire

    hey dude,

    thx for the nice script! i’m wandering though! my navigation is split in 2, mainNav is in the header.php and i’m calling your nice code (my subNav) into my index.php like that:

    the results are, when i click on a page in the mainNav that has subNav pages (lets say 3 for example) i’m seeing actually 4 pages (the mainNav page as a subNav page and my 3 subNav pages) and that’s fine, but.

    my 1st question is how could i rename the the mainNav page name that is automaticly shown in the subNav?

    and, my 2nd question (the most important) is how can i get the mainNav li.current_page_itme {} to remain active when i’m clicking on another subNav pages (that is not pulled from the mainNav)?

    thanks so much

    marc

  • morsefire

    oh,

    sry for bothering! i just found the answer to the 2nd question. i needed to add the following code to my CSS.

    .mainNav li.current_page_ancestor {}

    uuppps :)

  • http://www.creative-textile-and-quilting-arts.com Linda

    Just thought to let you know that an alternative way to do this is to use the code from the sidebar of the K2 Theme. Just strip away all the other stuff and you are left with a very nice piece of code and it’s a simple thing to make the heading clickable also if you want.

  • http://www.svennerberg.com Gabriel Svennerberg

    Linda: Cool! I will definitely check that out. Thanks for the tip!

  • http://www.petsnakesonline.com Pet Snakes

    Very interesting post, I love finding a good quality blog thats not full of rubbish. I would love to do a link exchange.

  • http://www.marnixbras.nl marnix bras

    Super script: Nice and well explained! Love that!
    One small thing: you mention:


    // Check to see if we have anything to output

    And that particular text shows up in my WordPress project :-) Because it is outside a PHP tag…
    Tanks for your blogpost!

  • http://www.svennerberg.com Gabriel Svennerberg

    marnix bras: Thanks for noticing! I’ve corrected the example.

  • http://www.data-creative.com Cian McCoy

    Hi

    Great post, pretty much exactly what I was looking for, thanks for that!

    I am very new to both wordpress and php so I’m sorry if this is a stupid question but I’m stuck
    The code that is output from this php looks something like his for each list item:

    Sub Page1

    Is it possible to insert a span within the anchor somehow? so the php would output something like this?

    Sub Page1

    Or would this make things complicated?

  • http://www.data-creative.com Cian McCoy

    sorry about the links in that last comment

    I basically want it to output something like this (ignore the extra spaces)

    Sub Page1

    hope it works this time

  • http://www.data-creative.com Cian McCoy

    Sorry for the flooding.. managed to insert a span in the anchors by using link_before and link_after like so


    if(is_page() && $post->post_parent) {
    // This is a subpage
    $children = wp_list_pages("title_li=&include=".$post->post_parent ."&link_before=&link_after=&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->post_parent ."&link_before=&link_after=&echo=0");
    } else if($has_subpages) {
    // This is a parent page that have subpages
    $children = wp_list_pages("title_li=&include=".$post->ID ."&link_before=&link_after=&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->ID ."&link_before=&link_after=&echo=0");
    }

    sorted!

  • http://www.environpestcontrol.co.uk Tim Dunkley

    Hi, Love the work you’ve done on this little submenu code, it was a piece of cake to add to my custom theme, however I’m just wondering how it would be possible to have the submenu output on a single horizontal line? (eg submenu1 | submenu2 | submenu3 | submenu4)

    Tim

  • http://www.svennerberg.com Gabriel Svennerberg

    @Tim: Your best bet is to use CSS to style the unordered list so that it’s horizontal. A now old, but still excellent article on how to do this is CSS Design: Taming Lists at A List Apart. http://www.alistapart.com/articles/taminglists/

    Hint: Try .submenu li {display: inline} in your CSS.

  • Shandilya

    Can someone help me with this? I have lots of articles and I want to display them under different parents. Here is my question:

    1. I want parent pages to be formatted differently than child page list

    For example in the link here: http://www.theagitator.com/publications/

    My parent page is called “Feature-Length Articles” [which is NOT a link and is formatted differently]

    Now I want to show all the children pages as links [] list with publish date.

    Thanks for all the help.

  • Silin

    What about having a completely seperated and with the submenu only showing when a main page has subpages, set the submenu still shows when a subpage has been clicked.

    That sounds much more difficult and is what I’m current working on.

  • Silin

    WordPress scrubbed my custom divs so that post didn’t make sense. Here it is again:

    “What about having a completely separated [div id=”menu][/div] and [div id=”submenu”][/div] with the submenu only showing when a main page has subpages, yet the submenu still shows when a subpage has been clicked.

    That sounds much more difficult and is what I’m current working on.”

  • eduardo-BR

    I love your script, is what a need, but all the sub-pages are showin in the search page, how can a fix this?

  • Pingback: wantusiak.com » Wordpress: How to style wp_list_pages

  • http://www.thinklabs.ca Nate Woods

    Just wanted to thank you for this, this works perfectly and was exactly what I was looking for.
    Thanks again.

  • suniel

    thanks, great post, works great!

  • http://www.spinnaweb.com.au/ Lloyd

    Thanks! This saved me heaps of time and worked straight out of the box. You’re a champion :)

  • Joshua Behshad

    Thank you. Worked for me first time.

  • http://www.briangervais.com Brian

    Thanks for the hack! Worked perfectly.

  • Jonas

    Thanks, you helped me allot!

  • Andrew

    Like many others, you’re help saved the day and I really appreciate you taking the time to share the knowledge!

    Cheers!

  • http://www.blogshoster.com reseller hosting

    I really like your writing style, its not generic and extremly long and tedious like a lot of blog posts I read, you get to the point and I really enjoy reading your articles! Oh, and merry Christmas!

  • http://www.pattayaresort.biz pattaya resort

    good thank you

  • http://howtobuildawebsiteshop.com David Spragu

    ID.’&echo=0′);

    if ($children) { ?>

    I found this shorter, simpler version you might like.

  • Shell

    This has worked perfectly for me, thanks! It is exactly what I have been searching for all day – after trying a lot of other suggestions thatall failed! Wish I had found this first! lol

  • http://natetharp.com Nate Tharp

    Thanks so much for this Gabriel. I’ve used a few different PHP snippets for secondary menus on various WordPress sites, but none of them were able to highlight the parent menu item when that page was active. But yours fixes that. Really appreciate it.

  • Michael Gad

    Hi!

    I a newbie at PHP and wordpress. I used you script and it works great!

    Now I have a problem though. How do I add a menulevel 3? So topmenu -> submenu ->subsubmenu

    It works displaying the subsubmenu but clicking the subsubmenu the other pages in the menu dissaper.

    Also i cannot figureout where i edit the for the subsubmenu.

    Anyway helt would be deeply appriciated.

    Thanks!

  • Michael Gad

    EDIT: Also i cannot figureout where i edit the UL for the subsubmenu.

  • Eddy

    Hello Gabriel Svennerberg, I am new in wordpress and I’m confused for using this now..
    maybe this is weird question, but want to ask, where I can put the code that you have written it? thx for the answer.

  • Pingback: How to style wp_list_pages « MalarVizhi

  • Pingback: WordPress Menus on the top « Amen

  • ravinder

    how to write main menu to submenus and sub-sub menus

  • maryn

    thanks for the code, it’s EXACTLY what I needed!

  • http://www.249designs.com/ fort collins web design company

    Great post. Over a year old and still useful. Thank you!.

  • babu

    NAme

  • Pingback: Incluir páginas y subpáginas en WordPress | SummArg

  • http://harrylesch.blogcenter.de Cisnero

    Hey There. I found your blog using msn. This is a really well written article. I?ll make sure to bookmark it and come back to read more of your useful information. Thanks for the post. I will definitely return.

  • http://www.thebeginnersworld.com Aravind

    Thanks, It really worked…

  • http://www.bootcampllandudno.com sarah

    Hi, This looks exactly what I am looking for, however I am completely alien to PHP.
    I understand I have to put the code within the page.php file, but I am not sure where exactly it goes. I have tried inserting it into various places but it doesn’t seem to work.

    Can somebody please let me know exactly where I place the code?

    And also if there is any other amendments that need to be done in any other files css etc?

    Thank you in advance.

  • Criks

    @Sarah,
    The example sais use it in page.php, altho i do not know if it is theme dependant
    When you look at page.php u see a reference to navigation.php
    “” in my case.
    Try adding the code WITHIN the menu-div on Navigation.php as that seemed to work for me.

    If experiencing trouble drop me a mail @ Dvermeer@ictmbo.nl

  • jasbir

    hi it is the great thing that we see here it give us the idea about the submenu

  • http://www.purnima.com.br Fernanda Scur

    Hi Gabriel,

    Many many thanks for this – it works wonderfully on my code. However, not being a php savvy ( I am just a designer) I encountered a problem, wondering if you might help.

    i am using custom templates on some of my subpages, and it seems your code does not recognizes them as subpages, so on those pages the submenu is not fetched. However, on the other pages using the default template (page.php) the submenu works great, including showing the link to the customized subpage….do you know how i could fix this?

    Thanks!

  • Brian

    Your code helped me to accomplish something I needed to do on a custom page template. Thanks.

    Do you know how I could edit this code to have it also output the post thumbnail for each of the pages in the list? I’m using the post thumbnail / featured post option built in to WordPress 3.1.

  • http://www.svennerberg.com/2009/02/creating-a-submenu-in-wordpress/ saleem

    very tuff.

  • John

    Perfect!!! thanks mate.

  • Justians

    Thanks ! This very helped me. :-)

  • http://richardvenancio.com Richard Venâncio

    If you want get submenu of menu WP, you can use this plugin: http://wordpress.org/extend/plugins/browse/new/

  • http://www.facebook.com/henkebarn Henke Blomqvist

    Hi Gabriel! Even though I’m swedish too, I’ll keep it in english.

    First of all: Thanks a lot!

    I’m doing a web site for a friend who’s starting a new tattoo studio. There are two artists there and I’d like to put it like this:

    Artists (parent)

    Artist #1 (child of Artists, parent of Tattoo style #1 & #2)

    Tattoo style #1 (child of Artist #1)
    Tattoo style #2 (child of Artist #1)

    Artist #2 (child of Artists, parent of Tattoo style #3 & #4)

    Tattoo style #3 (child of Artist #2)
    Tattoo style #4 (child of Artist #2)

    It works as is, but I want to hide the Tatto Styles when I’m on the Artists page. I only want to show one generation och children at a time, which means that when I go to the page Artists I want to show it’s children only: Artist #1 & #2. When I click on Artist #1 I only want to show it’s children, not Artist #2s children.

    Any thoughts?

  • http://www.facebook.com/henkebarn Henke Blomqvist

    And also, when I click on Tattoo style #1, #2, #3 or #4 it’s parent disappears. I Only get the main parent, Artists and the two children belonging to the middle parent (Artist #X) which has disappeared. The other middle parent is also missing.

  • Simon Pedersen

    This stumps me. I’ve used most of the day trying to find an example that says: “That’s easy, here’s what you do…” but all I find are articles like these

    http://wordpress.org/support/topic/wp_nav_menu-list-only-2nd-level-separate-submenu
    http://wordpress.stackexchange.com/questions/9504/is-it-still-not-possible-to-show-separate-submenu-with-custom-menu
    And your own article.

    It should make me angry – but I am more amazed. Separate submenus is practially in every website I develop – and you mean to say that the most used CMS system in the world doesn’t have a simple startLevel parameter in the wp_nav_menu function?

    Please tell me I am wrong!

    • http://twitter.com/Theis Theis Kofoed Hjorth

      A startLevel would be amazing. I have no clue how this can possibly be missing – it was much easier with list_pages, but somehow someone decided we didn’t need that kind of functionality anymore. Fools.

      I’ve been searching on/off for weeks for the right way to make relative childmenus in wordpress 3+, without luck.

      I am out of options.

  • http://twitter.com/CodeMonkeyG CodeMonkeyG

    This was perfect. Just the kind of script I was looking for. Your generosity is much appreciated.

  • Jacob V

    Just an update to your post.

    Function explained and lots of examples on wordpress: http://codex.wordpress.org/Function_Reference/wp_list_pages

  • lynn

    This is great. Thanks! I was wondering if you could help me figure out where to put the style tags. I’ve wrapped the entire code in a div tag, which works for the child pages, but I’d like to use my h2 style for the parent page, but I can’t figure out how to include that. Thanks so much!

  • Marc

    Brilliant, just what I was looking for. You saved my site!

  • Mohamed Shafiee

    Hi. This is Mohamed Shafiee here. Just wanted to share this because I want to make your lives easier with wordpress. My problem was displaying a list of menu items for a specific page or category. The menu was created by using wp_nav_menu(). I was able to achieve this, but it accesses the mysql database as I’m not that experienced with wordpress yet. It works like a charm. Here is the code:

    function get_sidemenu() {

    $page_id = @$_GET[‘page_id’];
    $cat = @$_GET[‘cat’];

    $id = $page_id . $cat;

    $sql = “SELECT caption, guid, post_id, menu_order FROM ((SELECT CONCAT(id, ‘-‘,(SELECT ‘post_type’)) AS pid, post_title AS caption, (SELECT ‘post_type’) AS type, guid FROM wp_posts) UNION (SELECT CONCAT(term_id, ‘-‘, (SELECT ‘taxonomy’)) AS pid, name AS caption, (SELECT ‘taxonomy’) AS type, CONCAT(‘./?cat=’, term_id) AS guid FROM wp_terms)) posts INNER JOIN (SELECT post_id, CONCAT(SUM(IF(meta_key=’_menu_item_object_id’, meta_value, 0)), ‘-‘, GROUP_CONCAT(IF(meta_key=’_menu_item_type’, meta_value, ”) SEPARATOR ”)) AS object_id, SUM(IF(meta_key=’_menu_item_menu_item_parent’, meta_value, 0)) AS parent FROM (SELECT post_id, meta_key, meta_value FROM (SELECT * FROM wp_postmeta WHERE post_id IN(SELECT post_id FROM wp_postmeta WHERE meta_key=’_menu_item_menu_item_parent’ AND meta_value=(SELECT meta_value FROM wp_postmeta WHERE post_id=(SELECT post_id FROM wp_postmeta WHERE meta_key=’_menu_item_object_id’ AND meta_value=[id]) AND meta_key=’_menu_item_menu_item_parent’))) k WHERE meta_key=’_menu_item_object_id’ OR meta_key=’_menu_item_menu_item_parent’ OR meta_key=’_menu_item_type’ ORDER BY post_id ASC) menu GROUP BY post_id) postmeta ON posts.pid = postmeta.object_id INNER JOIN (SELECT id, menu_order FROM wp_posts) pst ON postmeta.post_id=pst.id ORDER BY menu_order;”;

    $sql = str_replace(‘[id]’, $id, $sql);

    $res = mysql_query($sql);

    $html = ”;

    while ($arr = mysql_fetch_array($res)) {
    $html .= ‘‘ . $arr[‘caption’] . ‘‘;
    }
    $html .= ”;

    return $html;

    }

    Put the function in functions.php and use it where ever you need it. The SQL statement is not optimized yet coz I’m running out of time. Do you guys have any comments about this? If you do please email me. My email address is shafee_mohamed@hotmail.com.

  • Mohamed Shafiee

    I believe that wp_nav_menu should create a menu that can be accessed when creating themes don’t you think? I wouldn’t have had this problem if that was available.

  • http://www.ongoingworlds.com/ David Ball

    Thanks this was exactly what I needed, I couldn’t find a script for this anywhere!

  • Asdasdasd

    You have to check the var $post->ID
    If its not higher than 0 it can be a 404 Page.

    if (!$post->ID > 0) $has_subpages = false;

  • leguas

    Thank you very much, it works perfectly and help me a lot!

  • ajay

    Doesn’t work for me. Made my navbar disappear.

  • equinoxes

    How do we put the code in wordpress ?
    I am a novice
    Team – Equinox e Services – http://www.equinoxes.in