Custom Menu in WP 3.0+ (wp_nav_menu)

It is well known that WordPress 3.0 added support for custom menus (customizable menus). In my opinion, it is an extremely convenient and useful feature. Hence, this article.

The convenience lies in the fact that now you can create and configure menus directly from the admin panel by adding links with checkboxes and changing the order of links by simple drag and drop. In the menu, you can add links to pages, categories, and individual posts. It is also possible to create multi-level menus, and you can add your own custom links to the menu that WordPress does not know about. In general, there is complete freedom of action.

However, in order for this "freedom" to be available, it is necessary to easily set up the display of the custom menu in the template.

Using such menus will be extremely convenient when using the multisite feature of WordPress because you can configure different menus for different sites and use the same template for them.

Works through the nav_menu taxonomy, and custom (external) links are recorded in the wp_posts table. This approach is more flexible and dynamic, but requires constant generation of such menus.

So, let's get started.

Enabling support for custom menus (wp_nav_menu)

To begin with, it is necessary to register the ability to use custom menus and the menus themselves. This is done in the functions.php file, using the register_nav_menu() function, like this:

register_nav_menus( [
	'top'    => 'Top Menu',    //Menu location name in the template
	'bottom' => 'Bottom Menu'  //Another menu location name in the template
] );

Now we have registered 2 menus with the identifiers 'top' and 'bottom' with their respective names. The identifiers are needed to use them in the theme to specify the location where the menu created in the admin panel will be displayed using the wp_nav_menu() function. The names of the registered locations will be seen in the admin panel when we go to "Appearance -> Menus".

After the menus have been registered, go to the admin panel and create your menus (2 menus in this example):

  1. Set the menu name (the menu in the template can be displayed by the specified name, using the wp_nav_menu() function).

  2. Create menu items. Use the left column: pages, links, categories.

  3. Choose where the menu will be located, since we registered 2 menus, we will have 2 options: "Top Menu" and "Bottom Menu".
Creating a custom menu in WordPress. Admin panel.

Support for custom menus in WordPress is enabled separately for each theme with this line in the functions.php file: add_theme_support('menus'); However, this line is not necessary if we register menus. In this case, support will be enabled automatically.

Displaying custom menus using the wp_nav_menu function

The menus are registered and created, the next step is to add them to the template. This is done using the wp_nav_menu() function, which can take the following parameters:

wp_nav_menu( array(
	'menu'            => '',              // (string) The name of the menu to be displayed (specified in the admin panel when creating the menu, takes priority over the theme_location parameter - if specified, the theme_location parameter is ignored)
	'container'       => 'div',           // (string) Menu container. Wrapping ul. The container tag is specified (by default in the div tag)
	'container_class' => '',              // (string) Container class (div tag)
	'container_id'    => '',              // (string) Container id (div tag)
	'menu_class'      => 'menu',          // (string) The class of the menu itself (ul tag)
	'menu_id'         => '',              // (string) Menu id (ul tag)
	'echo'            => true,            // (boolean) Output to screen or return for processing
	'fallback_cb'     => 'wp_page_menu',  // (string) The function used as a fallback if the menu does not exist (could not be obtained)
	'before'          => '',              // (string) Text before the <a> of each link
	'after'           => '',              // (string) Text after the </a> of each link
	'link_before'     => '',              // (string) Text before the anchor of the link
	'link_after'      => '',              // (string) Text after the anchor of the link
	'depth'           => 0,               // (integer) Depth of nesting (0 - unlimited, 2 - two-level menu)
	'walker'          => '',              // (object) Class assembling the menu. Default: new Walker_Nav_Menu
	'theme_location'  => ''               // (string) Menu location in the template. (specified by the key with which the menu was registered in the register_nav_menus function)
) );

In this example, in the template, you should insert approximately (depending on the parameters you need) these 2 pieces of code:

#1 Displaying the menu by location

Top Menu. Insert in the header of the template (header.php), where the top menu will be displayed:

<?php
wp_nav_menu( array(
	'menu_class'=>'menu',
	'theme_location'=>'top',
	'after'=>' /'
) );
?>

This will display the menu created in the admin panel, attached to the "Top Menu" location, with a similar structure:

<div class='<WP classes>'>
	<ul class='menu'>
		<li><a class='<WP classes>' href="#">Link anchor</a> /</li>
		<li><a class='<WP classes>' href="#">Link anchor</a> /</li>
		<li><a class='<WP classes>' href="#">Link anchor</a> /</li>
	</ul>
</div>

Bottom Menu. Insert in the footer of the template (footer.php), where the bottom menu will be displayed:

<?php wp_nav_menu('menu_class=bmenu&theme_location=bottom'); ?>

This will display the menu created in the admin panel, attached to the "Bottom Menu" location. The structure will be identical to the first one.

Please note that in the first variant, the parameters were passed as an array. In the second case, they were passed as a string. Both variants are correct. This is common for WordPress functions - parameters can be passed as an array or a string (the string is then converted to an array).

#2 Displaying the menu by name

To display the menu by its name, you can use the 'menu' argument. The name is specified, which was given when creating the menu in the admin panel. In our example (see image), "Main Menu". The menu argument takes higher priority than the theme_location, so if we display by name, the theme_location parameter will be ignored.

<?php wp_nav_menu('menu=Main Menu'); ?>

You can specify the menu ID instead of the name. Thus, when the menu name is changed, the code will remain operational. You can view the menu ID in the URL during menu editing:

<?php wp_nav_menu('menu=455'); ?>

Notes

Removing the Div wrapper

You may have noticed that the menu is often wrapped with an unnecessary div tag. This can be removed by specifying an empty parameter 'container'=>'' in the arguments for the wp_nav_menu() function.

Changing default parameters

To avoid constantly specifying the same parameters for inserted menus, you can redefine them in functions.php. This is done through the filter wp_nav_menu_args:

register_nav_menus( array(
	'top'    => 'Top Menu',
	'bottom' => 'Bottom Menu'
) );

add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );
function my_wp_nav_menu_args( $args='' ){
	$args['container'] = '';
	return $args;
}

Similarly, you can create your own default arguments: $args['argument'] = 'value'.

Checking if the menu is registered

In WordPress, there is also a conditional function: has_nav_menu('top') - it checks whether the top menu location has been registered. If the menu is not specified, the wp_nav_menu() function will work like wp_list_pages(), but the "div" wrapper will remain, even though we removed it in the arguments. This issue can be resolved as follows:

if (has_nav_menu('top')){
  wp_nav_menu( array(
		'container' => '',
		'theme_location' => 'top',
		'menu_class' => 'menu')
	);
} else {
	echo '<ul class="menu">';
	wp_list_pages( array('depth' => 1, 'title_li' => '' ));
	echo '</ul>';
}

Walker parameter

Of all the passed arguments, the walker is the most unclear. For those who want to understand the purpose of this parameter, read the section in the description of the wp_nav_menu() function. It succinctly and clearly describes the principle. In short, it allows you to intervene in the menu generation process and modify it as you like.

Enabling additional menu parameters

The menu can be customized, for example, you can add the ability to specify a CSS class for the menu item, for the menu link. To do this, open the "Screen Options" tab: