As far as the WordPress community is concerned, developers tend to be divided on how plugins and themes should insert themselves into the WordPress menu.

On one hand, some feel that because WordPress offers the ability to insert custom menu items into existing menus, then plugin and theme content should follow suit and exist only in the predefined menu; however, others feel that because WordPress offers the ability to define custom menu items, then they should take advantage of it.

In fact, opinionated plugins have been written specifically for on this.

I don’t have a hard stance on this issue – though I tend to lean in the direction of adding new functionality to existing menus, I think that there are perfectly suitable cases for defining custom menus.

But if you’re going to define a custom menu item, then you should place it logically among the existing menu items: If you’re writing a theme, its menu should be placed near the ‘Appearance’ options rather than, say, the ‘Dashboard’ options. Then again, there are times where makes sense to isolate a menu item to its own section. In order to do this, a custom separator may need to be added in addition to the menu.

Adding a separator to the WordPress menu isn’t difficult and though there are a variety of ways to do it, the easiest way to do about doing so is using a couple of custom functions registered with the WordPress API.

Below, I give a working example for how to add a separator to the WordPress menu. I also look at how the default menu is structured, the proper way to add your own separator, and an example plugin for doing just that. If you’re an advanced developer, it may be more beneficial (and quicker!) for you to refer to the source code on GitHub.

Understanding The Default Menu

Before adding a separator to the WordPress menu, it’s important to understand how the default menu is structured. So, assuming that you’re looking at a default installation, the default menu is structured like this:

001Array
002(
003    [2] => Array
004        (
005            [0] => Dashboard
006            [1] => read
007            [2] => index.php
008            [3] =>
009            [4] => menu-top menu-top-first menu-icon-dashboard menu-top-last
010            [5] => menu-dashboard
011            [6] => div
012        )
013 
014    [4] => Array
015        (
016            [0] =>
017            [1] => read
018            [2] => separator1
019            [3] =>
020            [4] => wp-menu-separator
021        )
022 
023    [5] => Array
024        (
025            [0] => Posts
026            [1] => edit_posts
027            [2] => edit.php
028            [3] =>
029            [4] => open-if-no-js menu-top menu-icon-post menu-top-first
030            [5] => menu-posts
031            [6] => div
032        )
033 
034    [10] => Array
035        (
036            [0] => Media
037            [1] => upload_files
038            [2] => upload.php
039            [3] =>
040            [4] => menu-top menu-icon-media
041            [5] => menu-media
042            [6] => div
043        )
044 
045    [15] => Array
046        (
047            [0] => Links
048            [1] => manage_links
049            [2] => link-manager.php
050            [3] =>
051            [4] => menu-top menu-icon-links
052            [5] => menu-links
053            [6] => div
054        )
055 
056    [20] => Array
057        (
058            [0] => Pages
059            [1] => edit_pages
060            [2] => edit.php?post_type=page
061            [3] =>
062            [4] => menu-top menu-icon-page
063            [5] => menu-pages
064            [6] => div
065        )
066 
067    [25] => Array
068        (
069            [0] => Comments <span class="awaiting-mod count-0"><span class="pending-count">0</span></span>
070            [1] => edit_posts
071            [2] => edit-comments.php
072            [3] =>
073            [4] => menu-top menu-icon-comments menu-top-last menu-top-last
074            [5] => menu-comments
075            [6] => div
076        )
077 
078    [59] => Array
079        (
080            [0] =>
081            [1] => read
082            [2] => separator2
083            [3] =>
084            [4] => wp-menu-separator
085        )
086 
087    [60] => Array
088        (
089            [0] => Appearance
090            [1] => switch_themes
091            [2] => themes.php
092            [3] =>
093            [4] => menu-top menu-icon-appearance menu-top-first
094            [5] => menu-appearance
095            [6] => div
096        )
097 
098    [65] => Array
099        (
100            [0] => Plugins <span class="update-plugins count-0"><span class="plugin-count">0</span></span>
101            [1] => activate_plugins
102            [2] => plugins.php
103            [3] =>
104            [4] => menu-top menu-icon-plugins
105            [5] => menu-plugins
106            [6] => div
107        )
108 
109    [70] => Array
110        (
111            [0] => Users
112            [1] => list_users
113            [2] => users.php
114            [3] =>
115            [4] => menu-top menu-icon-users
116            [5] => menu-users
117            [6] => div
118        )
119 
120    [75] => Array
121        (
122            [0] => Tools
123            [1] => edit_posts
124            [2] => tools.php
125            [3] =>
126            [4] => menu-top menu-icon-tools
127            [5] => menu-tools
128            [6] => div
129        )
130 
131    [80] => Array
132        (
133            [0] => Settings
134            [1] => manage_options
135            [2] => options-general.php
136            [3] =>
137            [4] => menu-top menu-icon-settings menu-top-last
138            [5] => menu-settings
139            [6] => div
140        )
141 
142)

Simply put, the menu is an array of arrays. Looking at the code, it’s easy to see how the menu is organized. The key takeaway from this is that there are certain positions that are reserved for default menu items.

Specifically, the following positions are reserved:

  • 2. Dashboard
  • 4. Separator
  • 5. Posts
  • 10. Media
  • 15. Links
  • 20. Pages
  • 25.  Comments
  • 59. Separator
  • 60. Appearance
  • 65. Plugins
  • 70. Users
  • 75. Tools
  • 80. Settings

When adding a separator to the WordPress menu, avoid using any of the positions listed above.

Add a Separator To The WordPress Menu

A couple of functions will need to be written in order to add a custom separator. These can either be written within a theme’s functions.php file or a plugin file.

Registering a Custom Action

For adding a custom separator to the WordPress menu, a custom function will need to be registered with the WordPress API. This can be done using add_action. Below, I’m defining a custom action called ‘init_custom_menu_separator’ and a custom function named ‘add_admin_menu_separator.’

1add_action( 'admin_init', 'add_admin_menu_separator' );

The function itself looks like this:

01function add_admin_menu_separator( $position ) {
02 
03  global $menu;
04 
05  $menu[ $position ] = array(
06    0 => '',
07    1 => 'read',
08    2 => 'separator' . $position,
09    3 => '',
10    4 => 'wp-menu-separator'
11  );
12 
13}

Some points about the code:

  • The function accepts a single parameter – the position – this value represents where to insert the separator
  • The WordPress menu is defined at the global level so, in order to manipulate it, we must scope it within our function
  • We create our own array representing a separator. This is based on how existing menu separators are defined in the WordPress menu
  • We insert it into the WordPress menu using the position reference passed into the function

For those that are curious, you can learn more about what each index of the array represents by taking a look at the source code of the plugin on GitHub.

Running The Custom Action

At this point, the custom hook and functionality are defined for adding a custom separator to the WordPress menu, but there’s nothing invoking the actual function. To do this, one more action has to be defined.

So, I’ll register a new function using add_action and the WordPress admin_menu hook, thanks to a suggestion from Jamie.

My function is called ‘set_admin_separator.’ The add_action call is simple:

1add_action( 'admin_menu', 'set_admin_menu_separator' );

At this point, all that’s left is to define the actual function. It’s a single line:

1function set_admin_menu_separator() {
2  do_action( 'admin_init', 79 );
3} // end set_admin_menu_separator

This function uses WordPress’ do_action method for invoking the custom action that I defined earlier in the code. The second argument – 79 – is the value that’s passed into the function.

Basically, this function is telling WordPress to call the function that I’ve registered with the ‘admin_init’ hook (which is the ‘add_menu_separator’ function) and pass it the value of 79 (which will, in turn, be used as the position).

Once executed, this will add a custom menu separator above WordPress’ ‘Settings’ menu.

WordPress Custom Menu Separator Plugin

I’ve written a plugin that demonstrates all of the above code. You can grab it on GitHub. Once it’s installed, you can activate it in your Plugin’s dashboard page.

Just after activation (and assuming it’s a vanilla install of WordPress or no other plugins are interfering with the process) you should see a new separator appear above the ‘Settings’ menu.