Notice: get_settings is deprecated since version 2.1.0! Use get_option() instead. in /var/www/vhost/ on line 3840 Notice: get_settings is deprecated since version 2.1.0! Use get_option() instead. in /var/www/vhost/ on line 3840

I’ve also written a Custom Comment Walker function, after 3 hours of searching, I’m convinced it may be the only one out there lol…

If you’re trying to make a fancy navigation menu with CSS, you might have found the default IDs and classes applied by WordPress to the nav menu elements to be quite lacking. However, you can fix that by adding a custom walker function to add your own conditional classes. The custom nav walker function I made below will add the following CSS classes:

CSS Classes

  • .main-menu to top menu
  • .sub-menu to menus inside the main-menu
  • .menu-item to all <li>’s
  • .main-menu-item to all <li>’s in the main menu
  • .sub-menu-item to all <li>’s in a sub-menu
  • .sub-sub-menu to all menus inside a sub-menu
  • .menu-even/.menu-odd for <ul>’s (alternating)
  • .menu-depth-# to tell you how deep each menu is
  • .menu-item-even or .menu-item-odd (alternating)
  • .menu-item-depth-# to tell you which level menu the <li> is in
  • .menu-link to links

Optional Features

These requested features were can also be added to the code below. Click on each to be taken to the comment on this page describing how to integrate it.

This should make styling your Navigation Menus MUCH simpler.


You call the function by supplying the name in the $args when you call the wp_nav_menu function in you theme. So for example, to enable our walker, we would call the menu with this:

Print Registered Nav Menu

wp_nav_menu( array(
	'theme_location'	=> 'navigation_menu_primary',
	'container'		=> 'div',
	'container_id'		=> 'top-navigation-primary',
	'conatiner_class'	=> 'top-navigation',
	'menu_class'		=> 'menu main-menu menu-depth-0 menu-even', 
	'echo'			=> true,
	'items_wrap'		=> '<ul id="%1$s" class="%2$s">%3$s</ul>',
	'depth'			=> 10, 
	'walker'		=> new themeslug_walker_nav_menu
) ); // thanks nick

This would create the nav menu for the location “navigation_menu_secondary” give it a div wrapper with an id and class, and the first level menu will get the added classes main-menu, menu-depth-0, and menu-even. Now on to our custom walker function:

Custom Nav Menu Walker Function

class themeslug_walker_nav_menu extends Walker_Nav_Menu {
// add classes to ul sub-menus
function start_lvl( &$output, $depth ) {
    // depth dependent classes
    $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
    $display_depth = ( $depth + 1); // because it counts the first submenu as 0
    $classes = array(
        ( $display_depth % 2  ? 'menu-odd' : 'menu-even' ),
        ( $display_depth >=2 ? 'sub-sub-menu' : '' ),
        'menu-depth-' . $display_depth
    $class_names = implode( ' ', $classes );
    // build html
    $output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
// add main/sub classes to li's and links
 function start_el( &$output, $item, $depth, $args ) {
    global $wp_query;
    $indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
    // depth dependent classes
    $depth_classes = array(
        ( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
        ( $depth >=2 ? 'sub-sub-menu-item' : '' ),
        ( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
        'menu-item-depth-' . $depth
    $depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
    // passed classes
    $classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
    // build html
    $output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
    // link attributes
    $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
    $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
    $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
    $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
    $attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
    $item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
        apply_filters( 'the_title', $item->title, $item->ID ),
    // build html
    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

I hope this was helpful! If you have an improvement of this code, let me know and I’ll incorporate your changes (with credit, of course).

48 Thoughts on “Custom Nav Menu Walker Function

  1. Nick Nasty on September 26, 2012 at 7:20 am said:

    You have syntax error in first block of code. You are closing the array but not the function.

  2. Alejandro on October 24, 2012 at 10:05 pm said:

    Hello, thanks for sharing this code snippet.

    I am wondering if it’s possible to exclude one menu item based on its ID

    Thanks in advance.

    • bitacre on October 25, 2012 at 5:30 pm said:

      Sure, on the second line of the start_el() function (on the line right after global $wp_query;) insert the following code to skip a particular menu item:

      $id_to_skip = 44; 
      if( $item->ID == $id_to_skip ) 
          return $output;

      If you want to skip multiple IDs, use this slightly more complicated version:

      $ids_to_skip = array( 44, 45, 46 ); 
      foreach( $ids_to_skip as $id_to_skip )
          if( $item->ID == $id_to_skip ) 
              return $output;

      Hope this helps!

      • Alex V on January 17, 2013 at 3:26 pm said:

        Use K.I.S.S. everywhere…

        $ids_to_skip = array( 44, 45, 46 );
        if( in_array($item->ID, $ids_to_skip))
        return $output;

      • Your method removes top level item with matched ID but not its child element, is there any better method to filter out unwanted menu items ,I want to mark ‘remove’ in edit menu walker. using this parameter we can omit items from front end display without actually removing from admin menu edit section..

        • AnonymousHelper on August 5, 2017 at 10:53 am said:

          I know this reply is very late, but in case anyone comes here wondering how to do this:

          In the same manner post IDs are checked, parent post IDs can be checked as well:

          $id_to_skip = 44;
          if ($item->post_parent == $id_to_skip)
          return $output;

          This should filter out children of the filtered top level item.

  3. Alejandro on October 25, 2012 at 9:39 pm said:

    Thanks bitacre, works like a charm!

    I was wondering if there some way to pass the ID through the wp_nav_menu array.

    Thanks again!

  4. I’m trying to figure out the best solution to display the featured image (as a background) of each post, if the post doesn’t have a feature image it will set a default one. I can see it achievable using the walkers, any hints from where I should start?


    • bitacre on November 10, 2012 at 3:05 pm said:

      I can definetly give you a place to start:

      Anywhere in the start_el() function, you can determine if the current menu item has an associated thumbnail by using $bool = has_post_thumbnail( $item->ID);.

      That will return a boolean which you can craft into an if() statement to display the post thumbnail. I can’t get any more specific than that without knowing exactly how you want it displayed, but I’m sure you can figure it out using the post thumbnail functions, and item->ID to specify the post’s ID.

      Let me know if you get stuck and what you’re solution is, I may add this to the list of advanced options on this tutorial! Thanks!

      • Powla on May 2, 2013 at 1:54 am said:

        Can you use the above-mentioned to add post thumbnails to each of the sub-menu

      • items ?
  • Michael on April 9, 2014 at 5:00 am said:

    Hello! awesome! Thank you for this snippet.

    But I have one question. The sibling’s child node’s still remain with “display: none” inline style. But I want to just remove these hidden elements. Is that possible with this snippet?

  • A couple Questions:

    I’m wanting to add classes to the sub-menus. this seems like what I need…

    Is the Custom Nav Menu Walker Function supposed to be copied into the nav-menu-template.php?

    • bitacre on December 5, 2012 at 5:47 pm said:

      It’s a function, so it belongs in functions.php or in whatever sub-file is appropriate if you break down your functions into several different files and include them in functions.php. If you put it anywhere else, there’s no guarantee that template file will be loaded.

  • Hi,

    I’m trying to modify this with a function end_lvl to add a div to the end of the ul. The problem is that the div needs to contain unique content in each dropdown, so I’m trying to use $item->ID to find it! Here is my code:

    // add div to sub-menu
    function end_lvl( &$output, $item, $depth ) {
    global $wp_query;
    $indent = str_repeat(“\t”, $depth);

        if ($item->ID==37) {
            $output .= "$indent<div class=\"after-menu\">Stuff</div>\n</ul>\n";
        } else {
            $output .= "$indent</ul>\n";


    I can’t seem to use $item or the global query to add the item ID at any point. Do you have any suggestions?

    • Sorry the code didn’t paste very well:

      // add div to sub-menu
      function end_lvl( &$output, $item, $depth ) {
           global $wp_query;
          $indent = str_repeat("\t", $depth);
          if ($item->ID==37) {
              $output .= "$indent<div class=\"after-menu\">Stuff</div>\n</ul>\n";
          } else {
              $output .= "$indent</ul>\n";


  • How I can add a class only to the top level parent?

  • Pingback: Navigation Menus | Anand Verma

  • thanks dude.. 😀
    your snippet code make my life easier lol

  • hi bitacre,

    I am trying to add a class to the parent li the above code by samuel is adding the to second level li.

    And another thing is that how do I add an extra li to ad the seperator.

    Looking farward for your earliest response.


  • Pingback: Custom nav menu walker function « Wiki

  • Glitch on May 8, 2013 at 6:31 am said:

    Nice post. I would like to see one addition but I’m not sure if it’s possible at all. I would like to add a class to all

  • ‘s that contain a sub-sub-menu. Can it be done?
  • Pingback: wordpress wp_nav_menu()函数详解-wordpress

  • Pingback: » LOL +1

  • shishir umrao on August 3, 2013 at 2:17 am said:

    How to change a class of an item who has a sub-item ?

  • Pingback: How to add Class to <li> using wp_nav_menu() in Wordpress | Don't Be THAT Dev

  • As i try to get our Website work with wordpress I have a problem with our menu.
    I want to get the UL count with this code :

    function start_lvl( &$output, $depth = 0, $args = array() ) {
    $indent = str_repeat(“\t”, $depth);
    $output .= “\n$indent


    ul class=\”level-“.$depth.”\”>\n”;
    at nav-menu-template.php
    and it works very well !
    But unfortunately it starts again with the number 0 everytime another first level UL begins.
    I want to give each first level UL his own background, but with this solution they all have class level-0.
    Do you maybe have any ideas ?
    Thanky a lot
    You cann see the quellcode at

  • Pingback: How do I generate a custom menu/sub-menu system using wp_get_nav_menu_items in wordpress? - PHP Solutions - Developers Q & A

  • LindaJeanne on January 4, 2014 at 11:17 am said:

    Awesome! I was just beginning to research how to add a custom menu walker function, so that I could do exactly this — thanks for the time saved!

  • Stuart on March 8, 2014 at 9:37 pm said:

    Has something changed in WordPress? I was trying to create a walker in a theme I was writing and it wasn’t working so to test for errors I tried to implement your walker version as it’s used by WordPress as a practical example and still no success.

    After I tested my custom theme on my local machine running a WAMP server and then on my CentOS/cPanel server I decided to try a new install of WordPress and modify the code in the twentyeleven theme, but still the menu is not working.

    I’ve used the code exactly as is above in my functions.php file and called it in my header thus;

    wp_nav_menu( array( ‘theme_location’ => ‘primary’, ‘menu_class’ => ‘nav-menu’, ‘walker’ => new themeslug_walker_nav_menu ) );

    The output html I’m getting (with url removed) is

    Is anyone else having problems with this? I’m running WordPress 3.8.1


  • Stuart on March 8, 2014 at 9:42 pm said:

    Trying again with the html;

    <div class="nav-menu"> <ul> <li class="current_page_item"> <a href="">Home</a> </li> <li id="nav-menu-item-2" class="main-menu-item menu-item-even menu-item-depth-0 "> <a class="menu-link main-menu-link"> </a> </li> </ul> </div>

  • Pingback: wp_nav_menu | Wordpress中文站点

  • daniel on May 1, 2014 at 1:53 pm said:

    Hey I’m trying to use this walker code to add aria functionality. I was wondering if someone could help me figure out how to access data from the $item variable inside of the start_lvl function? Basically I’m not that great with PHP at the moment, so I don’t fully understand how the code is being executed, but with Aria menus the base link/a tag at depth of 0 needs to have an ID which I’m using the title of the item, that piece works in adding it to the base link/a tag, as well as all the other links/a tags, which is okay. I now need to get the attribute aria-labelledby to mirror the $item->title or ID of the a tag.
    Any help would be much appreciated.

    I’ve added this code to my start_el function:
    $item_name = esc_attr( $item->title );
    $attributes .= ! empty( $item->ID ) ? ‘ id=”‘ . esc_attr( $item->title ) .'”‘ : ”;

    And I’ve added this code to my start_lvl function:
    $output .= “\n” . $indent . ‘


    ul role=”menu” aria-labelledby=”‘ . $item_name . ‘” class=”‘ . $class_names . ‘”>’ . “\n”;

  • Thank you for this incredible resource. I’m trying to parse the information and have a question about assigning individual classes to the menu’s

  • ‘s. I tried going through the Menu > Advanced > Custom CSS portion in the WP backend, but they aren’t appearing in my menu items…just the default “page_item page-item-12 page_item_has_children” etc.
  • Would you have a way to assign specific classes to each menu item? I’m also wondering how I could insert custom HTML and an image into one of the

  • ‘s.
  • Thanks for any help!

  • Hey there, I know this post is a little old but I can’t seem to find any help for what I’m trying to accomplish.

    Basically, I’m trying to add language to my Nav Menu and its items.

    I basically need to wrap my element with and my

  • ‘s with
  • Any help would be much appreciated, I can’t seem to find any snippets online for this simple modification with the Walker_Nav_Menu

  • Hi Luke,

    I’m trying to adapt your walker but to no avail.

    What I’m trying to achieve is this output

    As you can see I’d like to clean up the output and only add ‘has-children’ to li and .sub-menu.

    I really hope you could point me the right way to create a new walker.


  • I know this is an old thread, but I need a pointer. I just want to add a


    div> after the


    ul> in wp_nav_menu. Do I need a whole walker class just to do that? or is there a simple one that achieves that? All my menu items are floated left so my div is collapsed. I need to clear the menu items.

  • Pingback: Simple Type : a minimalistic & typographic layout

  • Hey Luke thanks for the article, i

    if I just wanted to move what the link surrounds in my menu, so instead of it just surrounding the text have it surround the entire

  • would I just copy $item_output = sprintf( ‘%1$s<a%2$s>%3$s%4$s%5$s%6$s’, into my code?
  • Thanks

  • Thank you so much!!!

    I had a problem where I had menu links with the #scroll link attribute, and whenever one left the index.php to products page, the link would appear lets’ say “home/products/#contacts”, (even though #contacts on the main page worked perfectly) and it would be broken, wouldn’t scroll, not even go back to index.php.

    So what I had to do is a couple if statements…. didn’t find any other way but your MENU!!! Thank you so much, even though 5 lines turned into 99, but it worked! I just turned this line:

    $attributes .= ! empty( $item->url ) ? ‘ href=”‘ . esc_attr( $item->url ) .'”‘ : ”;


                                                                if ( is_home() ) {
                                    // This is the index
                                    $attributes .= ! empty( $item->url )     ? ' href="'     . esc_attr( $item->url        ) .'"' : '';
                                } else {
                                    // This is not the index
                                        if (! empty( $item->url ) && $item->url[0] != "#")  { 
                                        $attributes .=' href="'      . esc_attr( $item->url        ) .'"'; 
                                    } else if (! empty( $item->url ) && $item->url[0] == "#"){
                                        $attributes .=' href="' . home_url('/') . esc_attr( $item->url        ) .'"';

    if anyone knows any different method, would be glad to hear 🙂

  • Hi,

    I don’t see the closing tag for the


    ul class =”sub-menu> which is opened on line 17.
    I want to wrap a div around this ul.
    Could you please let me know how I can do this?

  • Perfect . I spend my 1 day on it. AFter that found this. Working perfect

  • I want to add class to First LI item how can i ?

  • Thank you for your solution – it worked like a charm ! 🙂

  • Pingback: How to use the US Web Design Standards with WordPress menus – molly moran

  • Brandon Powell on January 7, 2017 at 10:21 am said:

    I’m getting this error message saying –>

    Warning: Declaration of allb_walker_nav_menu::start_lvl(&$output, $depth) should be compatible with Walker_Nav_Menu::start_lvl(&$output, $depth = 0, $args = Array) in /Users/brandonpowell/sites/valet/wordpress-development/web/app/themes/sage-8.5.0/lib/walker.php on line 63

    Warning: Declaration of allb_walker_nav_menu::start_el(&$output, $item, $depth, $args) should be compatible with Walker_Nav_Menu::start_el(&$output, $item, $depth = 0, $args = Array, $id = 0) in /Users/brandonpowell/sites/valet/wordpress-development/web/app/themes/sage-8.5.0/lib/walker.php on line 63

  • Pingback: Add custom markup to Wordpress menu - ExceptionsHub

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    Post Navigation