One of the cool new features that was unveiled as part of WordPress 3 is the ability to create custom menus with a drag-and-drop interface, similar to the way widgets are added to “sidebars.” However, documentation for this new feature is still extremely sparse. After reading a handful of tutorials explaining small bits of the new feature, and performing a lot of trial-and-error testing, I finally got these new custom menus working with a new theme I was building.

The first step is to enable the custom menus within your theme. That is what I’ll try to cover in this article. In an upcoming article, I’ll discuss how to actually create the menus within WordPress and start using them.

Adding Menus to Your Theme

To begin with, you need to figure out where in your theme you want to add a new custom menu. For instance, let’s say you want to add a new menu right after your logo within the header area of your theme. Go to the theme editor, choose header.php and begin editing it.

Find your logo image within the theme file and add use the wp_nav_menu() function after it. I would recommend using code similar to the following.

<?php
if(function_exists('wp_nav_menu')) {
wp_nav_menu(array(
'theme_location' => 'top-nav',
'container' => '',
'container_id' => 'logo-inner',
'menu_id' => 'top-nav',
'fallback_cb' => 'topnav_fallback',
));
} else {
?>
<!-- Hard-coded menu -->
<ul id="top-nav" role="navigation">
<li><a href="#contact-us">Contact Us</a></li>
<li><a href="#log-in">Log In</a></li>
<li><a href="#about-us">About Us</a></li>
<li><a href="#home">Home</a></li>
</ul> <!-- #top-nav -->
<?php
}
?>

Now, let’s examine the code above.

To start with, we check to make sure that the wp_nav_menu() function exists (just in case this theme gets used in an installation of WordPress prior to 3.0). Assuming it does, we invoke the function.

The wp_nav_menu() function accepts quite a few different optional parameters. Following is a short description of each (as I understand them).

  • menu – The numerical ID, WordPress slug or full-text name of the menu you created within the Menus area of the WordPress administration area. Using this parameter will call a specific menu, rather than simply calling a generic menu location (explained in the ‘theme_location’ parameter below).
  • container – If you want to wrap the entire menu in an HTML container of some sort, indicate that here. I’m not sure what possible strings this accepts, but it defaults to “div.” Unfortunately, the menu generated by the wp_nav_menu() function appears to be hardcoded as an unordered list with HTML list items. Therefore, the container you indicate in this parameter will actually wrap around the <ul> item that’s auto-generated. As I mentioned, this parameter defaults to “div“, so, if you don’t want a container wrapper, you need to explicitly set this parameter to an empty string.
  • container_class – If you want to set a CSS class for the container element, you can declare that here. This property defaults to a dynamic value of “menu-{menu slug}-container”.
  • container_id – If you want to set a CSS ID for the container element, you can declare that here. Obviously, if you set the container to an empty string, you don’t need to specify a container_class or container_id, as they will be ignored.
  • menu_class – If you want to set a CSS class for the actual <ul> element used for the menu, declare that here. This defaults to simply “menu”.
  • menu_id – If you want to set a CSS ID for the actual <ul> element used for the menu, declare that here. This defaults to the menu slug with an auto-incremented numerical value (in case you use the same menu more than once on the page).
  • echo – Indicates whether the menu should be automatically echoed (printed) on the page or if you just want it returned as a PHP variable. This property defaults to boolean true (which means it will be echoed).
  • fallback_cb – the name of a function you want WordPress to call if the menu doesn’t exist or isn’t found for some reason.
  • before – any code you want to appear before each menu item (appears after the opening <li> tag for the menu item and after the opening <a> tag).
  • after – any code you want to appear after each menu item (appears before the closing </a> tag and before the closing </li> tag).
  • before_link – any code you want to appear beforeĀ each menu item (appears after the opening <li> tag for the menu item, but before the opening <a> tag).
  • after_link – any code you want to appear after each menu item (appears after the closing </a> tag but before the closing </li> tag).
  • depth – indicates how many hierarchical levels to display in the menu. This property defaults to 0, which means that all levels will be displayed. If your menu only has one level, you don’t have to worry about this, but if you create a multi-level custom menu, you may need to adjust this number.
  • walker – an actual WordPress “walker” object to use as the walker. I did not play with this property at all, so I’m not really sure what it does.
  • theme_location – This is the bread and butter of using custom menus. Instead of calling a specific menu by name, you can simply call a generic menu location. Then, within the Menus area of the administration center, you can choose which specific menu to show in each available menu location. Using the theme_location property instead of the menu property makes these menus more dynamic and easier to control through the WordPress GUI.

The next thing shown in the code above (after the closing braces for the wp_nav_menu() function and the if statement) is a hardcoded fallback. This hardcoded fallback is used if the wp_nav_menu() function doesn’t exist (not to be confused with the fallback_cb property you declared when calling wp_nav_menu(); though, you could certainly just call the same function you passed as the fallback_cb property, especially if you are using a well-supported, standard WordPress function like wp_list_pages() or something).

Update Your Functions File

The next thing you need to do is to update your functions file to add support for your new custom menus and to create the fallback function you’ll be using in case the menu doesn’t exist. To do that, you will use some code similar to the following.

<?php
add_action( 'after_setup_theme', 'regMyMenus' );
?>
function regMyMenus() {
// This theme uses wp_nav_menu() in four locations.
register_nav_menus( array(
'top-nav' => __( 'Top-Level Navigation' ),
'preferred-nav' => __( 'Preferred Navigation Tabs' ),
'donate-nav' => __( 'Donation Navigation Tabs' ),
'footer-links' => __( 'Footer Navigation Links' ),
) );
}

The code above will add a hook to WordPress that automatically registers your custom menu locations (theme_location) – not specific menus (those are created from within the “Menus” section of your admin panel) – don’t confuse the two.

To register each of these menu locations, simply use the add_action() method to instruct WordPress to call your custom function once the theme has loaded. Then, create a custom function that calls the register_nav_menus() method.

The register_nav_menus() function accepts an associative array as its only parameter. This associative array should use slug-compatible (no spaces, unconventional symbols, etc.; basically just alphanumeric characters and hyphens) IDs as the keys and user-friendly, full-text names for the menu locations as the values. You can register as many custom menus as you want with this function. The new default theme, Twenty-Ten, only uses one custom menu. The theme I was developing the other day actually utilizes four different custom menus.

Fallback Function

The next step is to create your fallback function that will be called if the menu hasn’t yet been created within your administrative area. Below, I’ve included the code for a sample. In this case, the fallback function simply outputs a hardcoded menu for testing purposes, but you can conceivably use any WordPress functions (wp_list_pages(), wp_get_archives(), etc.) within this custom function.

<?php
/**
* Top Nav Menu Fallback
*/
function topnav_fallback() {
?>
<!-- Fallback menu -->
<ul id="top-nav" role="navigation">
<li><a href="#contact-us">Contact Us</a></li>
<li><a href="#log-in">Log In</a></li>
<li><a href="#about-us">About Us</a></li>
<li><a href="#home">Home</a></li>
</ul> <!-- #top-nav -->
<?php
}
?>

You should create a custom fallback function for each menu location you create.

Conclusion

That’s all you need to do to actually add support for custom menus to your new theme. Later, we will explore how to set up these new menus within the administrative area of your WordPress system.