Improve your WordPress Navigation Menu Output

WordPress 3 has gone gold and ships with an amazing new menu manager that can be used to control the navigation menus of your website. This tutorial will teach you how to change the default output of this manager, since getting a custom output can heavily improve the style of your themes. So first of all here is an example of the wordpress menu we want to build.

How to display the content of the wordpress menu description field

As you can see, instead of a simple list we got the menu item name and below that name is a small description of that menu item. This is currently a rather popular style that unfortunatley can’t be done out of the box by wordpress.

As you may already know, once you have created a menu at your wordpress backend at Appearance > Menus you can use a wordpress function called wp_nav_menu() within your template files to display those menus.

The problem is, the basic output would look something like this:

<ul id="menu-main">
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
    <li><a href="#">Blog</a></li>

With a basic unordered list like that its almost impossible to create such a menu. So we need to change the output to get this:

<ul id="menu-main">
     <li><a href="#"><strong>Home</strong><span>Starting the journey</span></a></li>
     <li><a href="#"><strong>About</strong><span>What to expect</span></a></li>
     <li><a href="#"><strong>Contact</strong><span>Get in touch!</span></a></li>
     <li><a href="#"><strong>Blog</strong><span> Latest storys</span></a></li>

The <strong> tags wrap arround the title whereas the description is put into the <span> tags. Those can be styled easily with CSS later on to create this special menu style.

Preparing the backend

The first thing we need to do is to setup the menu properly in our backend. WordPress already comes with the option to add a description to each menu item, but it is hidden by default.

When you are at the Appearance > Menus Site you need to look at the top right and you will notice a “Screen Option” tab. Click it and you will get the option to display several other input fields for each menu item, among them a checkbox to show the description.

Once that is done,  if you start editing your items you will notice that you can now enter a description for each menu item.

By default wordpress adds a rather long description to menu items that are created by pages, I would recommend to just delete those enourmous novels and just add a few words just like I did.

Now that we have setup the data to display in our backend, its time to prepare the frontend to show that data.

Editing the output by using a custom walker

WordPress uses a special “Walker” class that iterates over each data record and then displays this record accordingly. The cool thing about that is that we can simply create our own custom walker extending that PHP class. That way we dont need to care about fetching the stuff from the database or preparing the data arrays. We only need to extend the part of the wordpress code that outputs the list. So open your functions.php file and add the following code:

class description_walker extends Walker_Nav_Menu
      function start_el(&$output, $item, $depth, $args)
           global $wp_query;
           $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

           $class_names = $value = '';

           $classes = empty( $item->classes ) ? array() : (array) $item->classes;

           $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
           $class_names = ' class="'. esc_attr( $class_names ) . '"';

           $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';

           $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        ) .'"' : '';

           $prepend = '<strong>';
           $append = '</strong>';
           $description  = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';

           if($depth != 0)
                     $description = $append = $prepend = "";

            $item_output = $args->before;
            $item_output .= '<a'. $attributes .'>';
            $item_output .= $args->link_before .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append;
            $item_output .= $description.$args->link_after;
            $item_output .= '</a>';
            $item_output .= $args->after;

            $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

So what does this Class do? This is basically the walker that wordpress is using (I just copied the source code) and added a few lines in the lower third for a better output: The walker now checks if a description is set, and if thats the case it wraps the description into a span tag and appends it to the navigation title.

The walker also checks if we are currently iterating over a sub menu item or a top level item, since our sub menu items do not need to display a description.

The Function Call

Now that we have created a custom walker we only need to tell wordpress that it should use our walker instead of its own. This can be easily done by calling the wp_nav_menu() with the walker parameter set:

wp_nav_menu( array(
 'container' =>false,
 'menu_class' => 'nav',
 'echo' => true,
 'before' => '',
 'after' => '',
 'link_before' => '',
 'link_after' => '',
 'depth' => 0,
 'walker' => new description_walker())

Thats it. Once that is done your menu will be output with a completly different code structure that you can easily style with CSS to fit your needs. Here is a short snippet to get you startet:


.nav a{
outline:medium none;
padding:2px 10px;
min-height: 35px;

.nav li a strong {

.nav li a span {

I hope you can utilize this knowledge to push the boundaries of beautiful wordpress generated menus ;)

60 replies
« Older Comments
  1. sports theme
    sports theme says:

    Hi,thanks for the post but I am having little problem I don’t know whether this is the right place to post the problem night be it is not related to it..I have created a custom menu to show the page in the navigation bar from WordPress but it does not show in the navigation bar..What to do?

  2. Genkisan
    Genkisan says:

    Thanks! Finally able to figure out how to identify the first and last menu item with this tutorial.

    To add a class to the first menu item:
    if ($item->menu_order == 1) {
    $classes[] = 'first';

    To add a class to the last menu item:
    if ($item->ID == get_theme_mod('lastmenuitem')) {
    $classes[] = 'last';

    and add this function to functions.php
    function mytheme_options() {
    global $wpdb;

    $topmenuid = get_theme_mod('nav_menu_locations');
    if ($topmenuid['top-menu'] != '0') {
    $menutermtax = $wpdb->get_var("SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = " . $topmenuid['top-menu']);

    $menuitem_post_ids = $wpdb->get_var("
    SELECT posts.ID
    FROM " . $wpdb->prefix . "posts AS posts
    SELECT object_id
    FROM " . $wpdb->prefix . "term_relationships
    WHERE term_taxonomy_id = " . $menutermtax . "
    ) AS termid ON termid.object_id = posts.ID
    ORDER BY posts.menu_order DESC
    LIMIT 1


    add_action('init', 'mytheme_options')

  3. Rj
    Rj says:

    Great tut, thanks for sharing, I was just wondering if it’s possible to use this in personal and commercial template?

    PS. I love all your work, keep it up!

  4. Shibi Kannan
    Shibi Kannan says:

    Excellent tutorial. I was using a theme that implements this walker menu but the output was all wrong. Finally realized there are description options in the wordpress menu backend after reading your tutorial. You saved me a lot of trouble. Just wondering if I can use this code in one of my upcoming themes.

  5. Laurent
    Laurent says:

    Hi Kriesi,

    It works for me,

    But I’m having an issue, I can’t re order the menu items in the Menu Administration,

    Any ideas ?

    Many thanks.

  6. sebastien
    sebastien says:

    it seems that $item->description=” ” by default (” ” and no “”)
    There is a space
    So, you write :
    $description = ! empty( $item->description ) ? ”.esc_attr( $item->description ).” : ”;

    but $item->description is never empty

    i’m adding :
    if ($item->description==” “) $item->description=””;

  7. eadearmas
    eadearmas says:

    i have a problem activating the jQuery Lightbox this is the error:

    Plugin could not be activated because it triggered a fatal error.

    Parse error: syntax error, unexpected T_STATIC, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or ‘}’ in /home/content/r/o/g/rogerplasencia/html/newsite/wp-content/plugins/jquery-lightbox-balupton-edition/jquery-lightbox.php on line 13

    i really appreciatte ur help. thanks

  8. eadearmas
    eadearmas says:

    im working with AVISIO templates, i need to change the portfolio template because i want to eliminate a columm that i dont need at all but i dont know how to do it. Could anybody help with this.
    thanks in advance.

Trackbacks & Pingbacks

  1. […] На мой взгляд главное меню сайта очень важная деталь, как в функцианальном плане , так и эстетическом, поэтому я всегда по мере возможностей старался сделать его более интересней и информативней. Улучшить меню моего пробного сайта работающего на WordPress 3 мне помог вот этот урок от Kriesi. […]

  2. […] На мой взгляд главное меню сайта очень важная деталь, как в функцианальном плане , так и эстетическом, поэтому я всегда по мере возможностей старался сделать его более интересней и информативней. Улучшить меню моего пробного сайта работающего на WordPress 3 мне помог вот этот урок от Kriesi. […]

« Older Comments

Comments are closed.