Skip to main content
WordPress made easy with the drag & drop Total WordPress Theme.Learn More

Adding Custom Attributes To WordPress Menus

January 5, 2018

They are so many tutorials on the web to explain “how to add a custom field to WordPress menus” but most of them treat about how to use the default CSS field of the default Description field. None of them gives a real method to add a real new field to menus. So, today we will create a simple plugin that will add a “subtitle” field to any WordPress menu.

Here is what the final result is in the WordPress administration:

Step 1: The Plugin Creation

Let’s go, as we saw in one of my previous post, creating a plugin isn’t complex. You just need to create a new folder under wp-content/plugins and called it “sweet-custom-menu”, and to create inside of it a file called “sweet-custom-menu.php”. Then open this file and add this code:

<?php
/*
Plugin Name: Sweet Custom Menu
Plugin URL: http://remicorson.com/sweet-custom-menu
Description: A little plugin to add attributes to WordPress menus
Version: 0.1
Author: Remi Corson
Author URI: http://remicorson.com
Contributors: corsonr
Text Domain: rc_scm
Domain Path: languages
*/

This code is all we need to define a new plugin. Now we are going to create a custom PHP class containing a constructor and the functions we will need to make the plugin work well.

Here is how to create the class:

class rc_sweet_custom_menu {

	/*--------------------------------------------*
	 * Constructor
	 *--------------------------------------------*/

	/**
	 * Initializes the plugin by setting localization, filters, and administration functions.
	 */
	function __construct() {

	} // end constructor

	/* All functions will be placed here */

}

// instantiate plugin's class
$GLOBALS['sweet_custom_menu'] = new rc_sweet_custom_menu();

The last line instantiates the class and add the whole class in a global variable.

Step 2: Adding Custom Fields Filter

Now that we have our class, we are going to create custom functions. The first function to add is the one that will register the “sub-title” custom field we want to be included as an advanced menu attribute. To do so, here the function to create, place this code instead of /* All functions will be placed here */ :

/**
 * Add custom fields to $item nav object
 * in order to be used in custom Walker
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
function rc_scm_add_custom_nav_fields( $menu_item ) {

    $menu_item->subtitle = get_post_meta( $menu_item->ID, '_menu_item_subtitle', true );
    return $menu_item;

}

Then, we need to tell WordPress to take our function into account, so place the code below in the __construct() function:

// add custom menu fields to menu
add_filter( 'wp_setup_nav_menu_item', array( $this, 'rc_scm_add_custom_nav_fields' ) );

Step 3: Saving Custom Fields

Even if the custom “sub-title” field doesn”t really exist we must create a function that will save its value on menu edition. Menu elements are in two wards custom post types, so we can use the custom post type APIs and post_meta method. So, to save custom menu field value, add this function under rc_scm_add_custom_nav_fields():

/**
 * Save menu custom fields
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
function rc_scm_update_custom_nav_fields( $menu_id, $menu_item_db_id, $args ) {

    // Check if element is properly sent
    if ( is_array( $_REQUEST['menu-item-subtitle']) ) {
        $subtitle_value = $_REQUEST['menu-item-subtitle'][$menu_item_db_id];
        update_post_meta( $menu_item_db_id, '_menu_item_subtitle', $subtitle_value );
    }

}

In this function, we are checking if the custom field value is sent from the form we ar about to create and then we simply store its value. Now we have to add the function to the appropriate hook. To do so, add this lines to __construct():

// save menu custom fields
add_action( 'wp_update_nav_menu_item', array( $this, 'rc_scm_update_custom_nav_fields'), 10, 3 );

Step 4: The Form

If you followed this tutorial step by step, you’ll probably guess that we haven’t created the form that must contain our sub-title field. This part is a bit more complex than the previous ones. This time we have to deal with Walker. I really encourage you to read the codex about the walker class, this will really help you to understand what it is and what it does. By the way they’re many great tutorials everywhere on the internet to give you more details about this class, so please check them out! In most part of the time, walkers are used to modify the HTML output of a menu. Here, we are working on the admin menu output form. Simply add this function to your main class:

/**
 * Define new Walker edit
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
function rc_scm_edit_walker($walker,$menu_id) {

    return 'Walker_Nav_Menu_Edit_Custom';

}

and then this to the constructor:

// edit menu walker
add_filter( 'wp_edit_nav_menu_walker', array( $this, 'rc_scm_edit_walker'), 10, 2 );

What this does is replacing the default admin edit menu form by a custom one. Now that the filter, is added, copy these two lines at the bottom of the sweet-custom-menu.php file, outside the rc_sweet_custom_menu class:

include_once( 'edit_custom_walker.php' );
include_once( 'custom_walker.php' );

We are about to include two files. The first one “edit_custom_walker.php” is the one that will modify the default form to edit the menu. It’s in this file that we are going to add the sub-title field.

The second one is the welker used on the website frontend, this is the file that will modify the menu output to your visitors.

As the “edit_custom_walker.php” is a bit long, i am not going to paste the whole code. You can view the whole code here. The only code i added to it is from line 174 to line 185. Here is the added code:

<p class="field-custom description description-wide">
<label for="edit-menu-item-subtitle-<?php echo $item_id; ?>">
<?php _e( 'Subtitle' ); ?><br />
<input type="text" id="edit-menu-item-subtitle-<?php echo $item_id; ?>" class="widefat code edit-menu-item-custom" name="menu-item-subtitle[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $item->subtitle ); ?>" />
</label>
</p>

If you want to add some more fields to the menu, simply duplicate these lines and copy & paste them. Once this step is done, the menu should be working in the administration. You should now be able to see the new “sub-title” field when adding a new element to a menu. If it’s not the case, make you followed the tutorial step by step. It’s now time time to output the subtitle value on the frontend.

Step 5: Custom Field Output

If everything is working fine in the administration, you probably now want to display the subtitle on the frontend. Open custom_walker.php and add thise code ot it:

<?php
/**
 * Custom Walker
 *
 * @access      public
 * @since       1.0 
 * @return      void
*/
class rc_scm_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 .= ' '.$item->subtitle.'</a>';
            $item_output .= $args->after;

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

This code is pretty standard. The only important thing here is these line:

$item_output .= ' '.$item->subtitle.'';

Step 6: Call The Walker!

In the last step you will need to open the header.php file in your theme folder. It’s where most part of the time the menu is called. If not, then contact the theme creator to know where it’s called.

Find the wp_nav_menu() function.

Then simply add the walker parameter:

'walker' => new rc_scm_walker

That’s it ! I hope you enjoyed adding a custom field to a WordPress menu. You can now modify a bit this code to add other fields such as checkboxes, select dropdown, textareas etc… Feel free to add a comment in the comments section. If you found a bug, if you improve it etc…

Sweet Custom Menu Plugin

If you are looking to implement this tutorial you can download the “Sweet Custom Menu Plugin” I’ve created to get you started. Enjoy!

Get The Plugin →

Article by Remi Guest Author
Published on: November 21, 2012
Last updated on: January 5, 2018
Subscribe to the Newsletter

Get our latest news, tutorials, guides, tips & deals delivered to your inbox.

49 Comments

  1. Gregg says:

    Remi, This is awesome! I had no idea you could do this. Thanks for the great tutorial.

  2. Mark says:

    Sounds good but missing a showcase. And got a question: Could I add in this way some more targets (_top) in menu link?

    • Remi says:

      Hi Mark, i sent the plugin to the WordPress official repo, so you can downolad and use it easily. And to answer your question, yes you can use this method to add links target (even id wordpress gives you by default a checkbox to open a link in a new window)

  3. Kev says:

    I tried this using a twenty-twelve child theme and get this error when I add the walker parameter to headers.php:

    Fatal error: Using $this when not in object context in …/wp-includes/class-wp-walker.php on line 185

    I’m pretty sure I followed the instructions that came with the plugin correctly.

  4. Decnoe says:

    Hi,Remi

    I got these notice after using debug bar plugin : Trying to get property of non-object online : 39 41 42 44 please help please.

    Best regards,
    Decneo

  5. Steven says:

    This might be asking a lot, but this is the closest I’ve come across in my research so far.

    I’m looking to add a data-reveal-id=”” field to my menu items (specifically one of them) to trigger a PopPop! (http://wordpress.org/extend/plugins/poppop/) modal window.

    I added the additional section to the edit_custom_walker.php file like you recommended:

    <label for="edit-menu-item-subtitle-“>

    <input type="text" id="edit-menu-item-subtitle-” class=”widefat code edit-menu-item-custom” name=”menu-item-subtitle[]” value=”data-reveal-id ); ?>” />

    And the attribute item appears in my Menu screen now, but I’m not sure how to modify this part to ensure that it actually outputs the field’s contents into the front-end of my site:

    $item_output .= ‘ ‘.$item->subtitle.”;

    Also, currently, whatever I type in the field reverts to a value of 0 when I save and replaces the subtitle value. Could be related to the above unmodified section, but I thought I’d post that too, in case it was indicative of me missing something else along the way.

    Thanks in advance if you get the opportunity to answer this.

    – Steven

    • Steven says:

      Never mind, I just used a ‘JQuery var newHTML =’ function to find and replace the menu item in question.

    • Rami Girgis says:

      I think it’s easier to edit the custom_walker.php file and remove $item_output .= ‘ ‘.$item->subtitle.”; (make sure to leave the close in) and then add a new line for $attributes above that (e.g. copy and paste $attributes .= ! empty( $item->xfn ) ? ‘ rel=”‘ . esc_attr( $item->xfn ) .'”‘ : ”; and then change xfn (two instances) to subtitle, and rel to data-reveal-id. Then in edit_custom_walker.php you can change the label “Subtitle” to “Data Reveal” or whatever you want, so that the label is appropriate in the form in the wp admin menu section

  6. Alex says:

    The plugin is not working. Like already said above it gives an error: Fatal error: Using $this when not in object context in /wp-includes/class-wp-walker.php on line 185.

    Do you have a working version?

  7. Alex says:

    Just to let other know, the description at http://wordpress.org/extend/plugins/sweet-custom-menu/installation/ is wrong, you should define the walker in wp_nav_menu like this: ‘walker’ => new rc_scm_walker

  8. julian says:

    awesome! now I can have everything I want on my menu

  9. Fernando says:

    I´m writting ‘walker’ => ‘rc_scm_walker’ and I get the error: Using $this when not in object context in /wp-includes/class-wp-walker.php on line 185.
    What I need to write or to change to make functional this plugin?

    • Rami Girgis says:

      Make sure you are using the same type of single quotes that are already being used in the other values/keys in the array, and then change ‘walker’ => ‘rc_scm_walker’ to ‘walker’ => new rc_scm_walker

  10. Ciprian Tepes (@cippo_ro) says:

    Thank you very much for your post! It really helped me out to build a color picker for each menu item.

    Cheers!

    • AJ Clarke | WPExplorer says:

      Nice! Would love to see what you came up with, is this for a theme/plugin available somewhere?

  11. Native Imaging says:

    ‘walker’ => new rc_scm_walker” is causing a parse error.. Can you post the code intact inside the wp_nav_menu() ?

  12. Rami Girgis says:

    Navtive Imaging: you are using the wrong type of quotes (not your fault, the code in the tutorials is using the wrong type). Make sure you manually type it in instead of copying and pasting. Or copy and paste an existing key/value from the array and just change the values to walker and new rc_scm_walker.

    • AJ Clarke | WPExplorer says:

      The issue is that when we updated to 3.6 recently it appears our syntax highlighter has stopped working. I’ll get that fixed up.

  13. ozenyxozzy says:

    Try:
    [code]$walker = new rc_scm_walker;[/code]
    and add that as

    [code]'walker' => $walker, [/code]

    This sholt fix it

  14. onilinkcr says:

    This may be a dumb question but can you call TWO Walkers or not? I mean one would be for your tutorial and the other I need for a description of the menu item.

    Possible?

  15. Lucas says:

    Hi, I have been trying to add a custom checkbox but it isn’t working, any tips creating the custom meta or updating the custom meta for a checkbox would be awesome.

    Thanks in advance

  16. vivin says:

    Its nice to to add sub-menu field , same way we can add thumbnail-image url input field to menu items , As we know on menu edit admin page ,we mostly shuffle the position of items by sorting menu list. so parent menu ,sub-menu relation changes on every instance of sorting. so how can we add custom field to top-level items only and not the sub-menu items.

    • AJ Clarke says:

      Hi Vivin, I’m not actually sure how to do that, but if you find out, please let me know! That sounds really handy.

  17. Matt Cromwell says:

    Thanks for a great write-up. I used this tutorial to create an enahanced “Target” field that uses a dropdown to target FooBox from the menu. I also updated the code to use WP 4.0 Walker class. Check it out here:
    https://github.com/mathetos/target-foobox

  18. Olof Larsson says:

    I used this instead of rewriting all that html, I haven’t tested it yet, but I hope it works 🙂

    Cheers!

    [code]&lt;?php
    include_once( ABSPATH . &#039;wp-admin/includes/nav-menu.php&#039; );

    class CustomAdminMenuWalker extends Walker_Nav_Menu_Edit{
    public function start_el( &amp;$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    parent::start_el($output, $item, $depth, $args, $id);
    $needle = &#039;';
    $pos = strrpos($output, (string)$needle);
    $start = substr($output, 0, $pos-1);
    $end = substr($output, $pos-1, strlen($output)-1);

    $output = $start.$this-&gt;addCustomFields().$end;
    }
    public function addCustomFields(){
    return "Baaam";
    }
    }[/code]

  19. Mathew says:

    Can use this in my theme in commercial use?

  20. Rizwan says:

    Hello, thanks for this great plugin but I think it is not compatible with wordpress 4.1 because I got a fatal error at front end after installing this plugin. Can you fix it please? I downloaded the plugin from wordpress.org. thanks

  21. Bareera Ahmed says:

    Hi, Thank you for this tip, very good. I would like to know if there’s any way to repeat this way to make more menus? I tried but it’s bring me an error. Thanks in advance!

  22. stuk88 says:

    Please fix the code.:
    ‘walker’ => ‘rc_scm_walker’
    is supposed to be
    ‘walker’ => new rc_scm_walker

  23. Naroa says:

    “As the “edit_custom_walker.php” is a bit long, i am not going to paste the whole code. You can download the file here.”

    Sorry, where can I download it?

  24. meysam98 says:

    Hi and thanks for this, i am using wp_bootstrap_navwalker in my theme and this code have conflict. Please help me, how i solve it.

    • AJ Clarke says:

      That’s because you are already using a walker “wp_bootstrap_navwalker” so following this guide won’t work. You will have to instead modify the existing “wp_bootstrap_navwalker” to work with your custom attributes.

  25. eavitan says:

    Hey, i tried this great tutorial on one of my website.

    when im trying it on a default wordpress theme it works, but when im trying it on a premium theme it doesnt work.

    i didnt got to the part when i try to execute the custom field in the frontend, for now im just trying to display the custom field in the backend.

    when checking in the theme header.php i noticed that there is already a walker (‘walker’ => new Pojo_Navbar_Nav_Walker() ), is this the reason why it ignores the input display in the backend?
    is do, how can i overide it?

    thanks, and keep going with great tutorials!

    • AJ Clarke says:

      If the theme is using a custom walker than it won’t work because the guide shows you how to create a custom walker to show your fields. Since you are using a premium theme though you should be able to contact the developer so they can assist you either modifying the current walker or at least showing you how you could replace it via a child theme wiht your custom one.

Leave a Reply

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