Enhancing WordPress Custom Menus for Navigation

Updated on June 05, 2017

Custom navigation menus are an amazing WordPress feature that allows admin users to add any list of links anywhere into the site. They can be links to internal resources, like posts, pages and archives, as well as links to external resources, like your social profiles pages. WordPress reserves a specific admin page to custom navigation menus. In this page the admin user can create menus, arrange items and set properties which affect the way they appear in the front-end.

WordPress Appearance Menus Screen

WordPress Appearance Menus Screen

That being said, the main questions behind this post is: “Can we extend the built-in functionalities of the Menus Screen?”
Of course we can. So I will dive deep into the topic, and I will dissect sections and objects that allow to add and manage menus and their items. But it won’t be just a description of the screen. I will show you how to add custom items to the accordion menu placed in the left sidebar.

These custom boxes will show additional lists of links to choose from when building custom navigation menus. More specifically, I will show you how to add a box containing links to the site authors’ archives.
Let’s start diving into building a WordPress custom menu.

The Appearance Menus Screen

The Menus editing page allows users to create and manage custom menus thanks to an intuitive interface. The Screen Options button in the upper right corner unveils the first section of the page which contains two groups of checkboxes. The Boxes group allows the user to hide and show menu meta boxes in the left sidebar, while the Show advanced menu properties group hides and shows properties like link targets, descriptions and CSS classes for list items.

Screen Options Tab

Screen Options Tab

Once the first menu has been created, the Menus Screen looks a bit different from its default appearance. New sections appear in the Edit Menus tab.

  • The top section provides a select menu to choose from and a create a new menu link;
  • The left column holds an accordion menu containing the available menu item meta boxes. These meta boxes contain items selected from pages, posts, categories, tags, taxonomies, post types and post formats (shortly, custom post types and taxonomies).
  • The Menu Structure section holds the menu item modules from which the user can arrange links and set their attributes and properties.
  • The Menu Settings section provides a list of check boxes that link menus to registered theme locations.
The built-in menu item meta boxes show lists of links pointing to site content

The built-in menu item meta boxes show lists of links pointing to site content

Meta Boxes and Modules

The left sidebar of Menus Screen holds an accordion menu whose elements are named menu item meta boxes. These blocks provide a number of form fields and controls that allow the user to search the site content and add links to navigation menus.
By default WordPress provides the following menu item meta boxes:

  • pages
  • posts
  • custom links
  • categories
  • tags
  • post formats
  • custom post types
  • custom taxonomies

When the user adds a link to a menu, a new item module is appended to the Menu Structure section of the screen. WordPress provides four typologies of menu item modules:

  • Post Type module (posts, pages and custom post types);
  • Post Type Archive module;
  • Taxonomy module (for categories, tags, custom taxonomies and post formats);
  • Custom Links module.
Menu item modules compared

Menu item modules compared

We are going to to extend the built-in structure of the Appearance Menus Screen with custom meta boxes that would allow the admin user to select pieces of content not available by default, like author archives.
Unfortunately, the Core Navigation Menu API is not documented and to achieve our goal it was necessary to dissect line by line two core files: /wp-admin/includes/nav-menu.php for menu meta boxes, and /wp-includes/nav-menu.php for menu item modules.
Let’s start coding.

Create a WordPress Custom Menu Item Meta Box

Create a new plugin and add the following code to the main file:

/**
 * Add menu meta box
 *
 * @param object $object The meta box object
 * @link https://developer.wordpress.org/reference/functions/add_meta_box/
 */
function custom_add_menu_meta_box( $object ) {
	add_meta_box( 'custom-menu-metabox', __( 'Authors' ), 'custom_menu_meta_box', 'nav-menus', 'side', 'default' );
	return $object;
}
add_filter( 'nav_menu_meta_box_object', 'custom_add_menu_meta_box', 10, 1);

The WordPress add_meta_box function registers meta boxes in admin pages. It keeps the following arguments:

Argument Type Description
$id string (required) the meta box ID
$callback callable (required) the function that echoes the meta box content
$screen string|array|WP_Screen (optional) the screen on which to show the meta box
$context string (optional) the context within the screen where the meta box shloud display (possible values are 'normal', 'side', 'advanced', defaults to 'advanced')
$priority string (optional) possible values 'high', 'low', 'default'
$callback_args array (optional) an array of arguments to be passed to the callback

Maybe you would have expected the add_meta_box function would be hooked to an action like add_meta_boxes, which is triggered in post editing pages. Unfortunately, we don’t have an action in nav-menus.php we can hook the function to, so we’re forced to use the nav_menu_meta_box_object filter. This hook determines whether a menu item meta box will be added for an object type. When the filter runs, add_meta_box registers the custom meta box.
Now we can define the callback function which will produce the HTML content for the meta box.

/**
 * Displays a metabox for an author menu item.
 *
 * @global int|string $nav_menu_selected_id (id, name or slug) of the currently-selected menu
 */
function custom_menu_meta_box(){
	global $nav_menu_selected_id;
	$walker = new Walker_Nav_Menu_Checklist();
	...
}

The global variable keeps memory of the current menu ID, while $walker stores a new instance of Walker_Nav_Menu_Checklist object, which builds the HTML list of menu items.
Then, we have to set the value of $current_tab variable which will determine the active tab in the meta box. To keep things easy, I will provide just two tabs:

$current_tab = 'all';
if ( isset( $_REQUEST['authorarchive-tab'] ) && 'admins' == $_REQUEST['authorarchive-tab'] ) {
	$current_tab = 'admins';
}elseif ( isset( $_REQUEST['authorarchive-tab'] ) && 'all' == $_REQUEST['authorarchive-tab'] ) {
	$current_tab = 'all';
}

Following, we will get all users with write privileges, adding a number of properties to the $authors object.

$authors = get_users( array( 'orderby' => 'nicename', 'order' => 'ASC', 'who' => 'authors' ) );
$admins = array();

/* set values to required item properties */
foreach ( $authors as &$author ) {
	$author->classes = array();
	$author->type = 'custom';
	$author->object_id = $author->nickname;
	$author->title = $author->nickname . ' - ' . implode(', ', $author->roles);
	$author->object = 'custom';
	$author->url = get_author_posts_url( $author->ID ); 
	$author->attr_title = $author->displayname;
	if( $author->has_cap( 'edit_users' ) ){
		$admins[] = $author;
	}
}
$removed_args = array( 'action', 'customlink-tab', 'edit-menu-item', 'menu-item', 'page-tab', '_wpnonce' );
?>

get_users returns an array of $user objects selected by the specified parameters. The who param forces WordPress to query the database just for users with writing privileges.
$admins array will store an array of authors, while the latest variable, $removed_args, will store a list of query vars to be removed.
When we’re done with data, we can print the meta box mark-up. First we’ll build tab labels and links:

<div id="authorarchive" class="categorydiv">
	<ul id="authorarchive-tabs" class="authorarchive-tabs add-menu-item-tabs">
		<li <?php echo ( 'all' == $current_tab ? ' class="tabs"' : '' ); ?>>
			<a class="nav-tab-link" data-type="tabs-panel-authorarchive-all" href="<?php if ( $nav_menu_selected_id ) echo esc_url( add_query_arg( 'authorarchive-tab', 'all', remove_query_arg( $removed_args ) ) ); ?>#tabs-panel-authorarchive-all">
				<?php _e( 'View All' ); ?>
			</a>
		</li><!-- /.tabs -->

		<li <?php echo ( 'admins' == $current_tab ? ' class="tabs"' : '' ); ?>>
			<a class="nav-tab-link" data-type="tabs-panel-authorarchive-admins" href="<?php if ( $nav_menu_selected_id ) echo esc_url( add_query_arg( 'authorarchive-tab', 'admins', remove_query_arg( $removed_args ) ) ); ?>#tabs-panel-authorarchive-admins">
				<?php _e( 'Admins' ); ?>
			</a>
		</li><!-- /.tabs -->
	</ul>

We have to be careful and assign the correct class names, IDs and data attributes to the elements of the meta box. In case of error, the accordion menu won’t work properly.
We used add_query_arg and remove_query_arg functions to set tab specific values for authorarchive-tab query var and remove unnecessary query variables.
The image below shows the resulting tab labels.

wordpress custom menu tabs for authors

wordpress custom menu tabs for authors

Next, we will build the HTML content of the tabs (again, pay attention to class names and IDs):

<div id="tabs-panel-authorarchive-all" class="tabs-panel tabs-panel-view-all <?php echo ( 'all' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>">
	<ul id="authorarchive-checklist-all" class="categorychecklist form-no-clear">
	<?php
		echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $authors), 0, (object) array( 'walker' => $walker) );
	?>
	</ul>
</div><!-- /.tabs-panel -->

<div id="tabs-panel-authorarchive-admins" class="tabs-panel tabs-panel-view-admins <?php echo ( 'admins' == $current_tab ? 'tabs-panel-active' : 'tabs-panel-inactive' ); ?>">
	<ul id="authorarchive-checklist-admins" class="categorychecklist form-no-clear">
	<?php
		echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $admins), 0, (object) array( 'walker' => $walker) );
	?>
	</ul>
</div><!-- /.tabs-panel -->

Each tab contains a list of checkboxes. This list is printed by the walk_nav_menu_tree function, which keeps the following three arguments:

Argument Type Description
$items array (required) An array of menu items
$depth int (required) How many levels of the hierarchy are to be included
$r object (required) Instance of a custom Walker_Nav_Menu_Checklist class

$items stores the array of admin users. The array_map function applies the wp_setup_nav_menu_item function to $admins, adding menu item properties to the array elements.
The following image shows the resulting checklist of authors.

The first tab in the checkbox will show the full list of authors with writing capability

The first tab in the checkbox will show the full list of authors with writing capability

Now we can add the submit button and a spinner.

<p class="button-controls wp-clearfix">
	<span class="list-controls">
		<a href="<?php echo esc_url( add_query_arg( array( 'authorarchive-tab' => 'all', 'selectall' => 1, ), remove_query_arg( $removed_args ) )); ?>#authorarchive" class="select-all"><?php _e('Select All'); ?></a>
	</span>
	<span class="add-to-menu">
		<input type="submit"<?php wp_nav_menu_disabled_check( $nav_menu_selected_id ); ?> class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e('Add to Menu'); ?>" name="add-authorarchive-menu-item" id="submit-authorarchive" />
		<span class="spinner"></span>
	</span>
</p>

</div><!-- /.categorydiv -->
<?php
}

The link in the first span element allows to select all check boxes in the current list, thanks to the query argument selectall. The second span contains a submit button which will be disabled by wp_nav_menu_disabled_check function if no items are checked.
Finally, the span.spinner element adds a spinner icon.
The Authors Meta Box is complete, and we can appreciate the result of our hard work in the following image.

The final meta box will allow the admin user to add links to author archives

The final meta box will allow the admin user to add links to author archives

Conclusions

WordPress Core Navigation Menu API is a set of core functions which create sections and objects in Appearance Menus Screen. Unfortunately, this API is not documented in the Codex, and the only way to learn how to change the Menus Screen functionalities is an in-depth dissection of /wp-admin/includes/nav-menu.php file.
Same problem with menu item modules. How are they structured? How many typologies are available? Can we create new typologies? No answers to these questions in the Codex. At the time of writing, the only way to better understand item modules is to analyze /wp-admin/includes/nav-menu.php core file, and the wp_setup_nav_menu_item function (line 730 in WordPress 4.5).
For these reasons, this post can be considered a first approach to this topic, and I hope you readers would leave your suggestions and ideas for discussion in the comments below.

The full code of our example WordPress custom menu plugin is available in a public gist.

This article was written by Carlo Daniele

Carlo is a freelance front-end designer and developer. When he writes articles and tutorials, Carlo mainly deals with web standards, but when he plays with websites his best workmate is WordPress.

Hand-picked related articles

  1. Gravatar for this comment's author
    Ronald February 5, 2017 at 5:00 am

    Great tutorial, Carlo! I’ve downloaded the plugin from github.
    It was just what I needed on https://www.worldofwp.com
    Thank you!

    1. Gravatar for this comment's author
      Carlo Daniele February 7, 2017 at 8:21 am

      Glad to be of help

  2. Gravatar for this comment's author
    barun October 9, 2017 at 4:49 am

    its a great article.
    But I want to add meta fields in Dashboard nav-menu taxonomy. I mean the field box will appear in menu edit area.
    This is for my website’s custom mega menu. Please help me.

Leave a Reply to barun Cancel reply

Use WordPress?

Join 20,000+ others who get our FREE weekly newsletter with WordPress tips on how to drive more traffic and revenue to your business!

You have Successfully Subscribed!

Send this to a friend