Add a Separator To The WordPress Menu

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:

Array
(
    [2] => Array
        (
            [0] => Dashboard
            [1] => read
            [2] => index.php
            [3] =>
            [4] => menu-top menu-top-first menu-icon-dashboard menu-top-last
            [5] => menu-dashboard
            [6] => div
        )

    [4] => Array
        (
            [0] =>
            [1] => read
            [2] => separator1
            [3] =>
            [4] => wp-menu-separator
        )

    [5] => Array
        (
            [0] => Posts
            [1] => edit_posts
            [2] => edit.php
            [3] =>
            [4] => open-if-no-js menu-top menu-icon-post menu-top-first
            [5] => menu-posts
            [6] => div
        )

    [10] => Array
        (
            [0] => Media
            [1] => upload_files
            [2] => upload.php
            [3] =>
            [4] => menu-top menu-icon-media
            [5] => menu-media
            [6] => div
        )

    [15] => Array
        (
            [0] => Links
            [1] => manage_links
            [2] => link-manager.php
            [3] =>
            [4] => menu-top menu-icon-links
            [5] => menu-links
            [6] => div
        )

    [20] => Array
        (
            [0] => Pages
            [1] => edit_pages
            [2] => edit.php?post_type=page
            [3] =>
            [4] => menu-top menu-icon-page
            [5] => menu-pages
            [6] => div
        )

    [25] => Array
        (
            [0] => Comments <span class="awaiting-mod count-0"><span class="pending-count">0</span></span>
            [1] => edit_posts
            [2] => edit-comments.php
            [3] =>
            [4] => menu-top menu-icon-comments menu-top-last menu-top-last
            [5] => menu-comments
            [6] => div
        )

    [59] => Array
        (
            [0] =>
            [1] => read
            [2] => separator2
            [3] =>
            [4] => wp-menu-separator
        )

    [60] => Array
        (
            [0] => Appearance
            [1] => switch_themes
            [2] => themes.php
            [3] =>
            [4] => menu-top menu-icon-appearance menu-top-first
            [5] => menu-appearance
            [6] => div
        )

    [65] => Array
        (
            [0] => Plugins <span class="update-plugins count-0"><span class="plugin-count">0</span></span>
            [1] => activate_plugins
            [2] => plugins.php
            [3] =>
            [4] => menu-top menu-icon-plugins
            [5] => menu-plugins
            [6] => div
        )

    [70] => Array
        (
            [0] => Users
            [1] => list_users
            [2] => users.php
            [3] =>
            [4] => menu-top menu-icon-users
            [5] => menu-users
            [6] => div
        )

    [75] => Array
        (
            [0] => Tools
            [1] => edit_posts
            [2] => tools.php
            [3] =>
            [4] => menu-top menu-icon-tools
            [5] => menu-tools
            [6] => div
        )

    [80] => Array
        (
            [0] => Settings
            [1] => manage_options
            [2] => options-general.php
            [3] =>
            [4] => menu-top menu-icon-settings menu-top-last
            [5] => menu-settings
            [6] => div
        )

)

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.’

add_action( 'admin_init', 'add_admin_menu_separator' );

The function itself looks like this:

function add_admin_menu_separator( $position ) {

	global $menu;

	$menu[ $position ] = array(
		0	=>	'',
		1	=>	'read',
		2	=>	'separator' . $position,
		3	=>	'',
		4	=>	'wp-menu-separator'
	);

}

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:

add_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:

function set_admin_menu_separator() {
	do_action( 'admin_init', 79 );
} // 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.

31 Replies to “Add a Separator To The WordPress Menu”

  1. Thanks Tom, a well written plugin.

    So this isn’t called on every page using the init hook, you can use:
    add_action( ‘admin_menu’, ‘set_admin_menu_separator’ );

  2. I’ve shortened it even further and added this to functions.php:


    add_action( 'admin_menu', 'set_admin_menu_separator' );
    function set_admin_menu_separator() {
    $position = 79;
    global $menu;
    $menu[$position] = array(
    0 => '',
    1 => 'read',
    2 => 'separator' . $position,
    3 => '',
    4 => 'wp-menu-separator'
    );
    }

  3. Neat! Thanks Tom.

    On a side note, this doesn’t work for me in a fresh install of WP 3.5.1 using add_action( ‘admin_menu’, ‘set_admin_menu_separator’ ); (as per Jamie’s suggestion and your updated article).

    It only works when using add_action( ‘init’, ‘set_admin_menu_separator’ );

  4. Wow, you both responded super fast. :)

    @Jamie: If I pop that code into my functions.php yes it does work. It creates a sub-menu in my Settings for “wpautop control”

  5. Hi Jamie,

    I totally derped and literally just copied/pasted the code showed above (without making modifications to try to get it to work with a separator).

    For the life of me all the ways that I tried the original code coupled with admin_menu or using add_menu_page would not display that darned separator. The only way I got it to work was using init.

    I stumbled on this link doing a while doing a search for ways to get that sucker to display: https://gist.github.com/franz-josef-kaiser/4693195

    It appears to work using admin_menu and add_menu_page/add_submenu_page similar to the last sample you posted.

    I also found this link: http://www.xldstudios.com/adding-a-separator-to-the-wordpress-admin-menu/

    The above has a smaller amount of code but the only problem there is it won’t put a separator at say 81 (after Settings).

  6. Hi Tom,

    Just thought I’d post that I tried to use your plugin from GitHub on a site (as a test) and it doesn’t appear to be working. It seems to clash with plugins such as WooCommerce (and a few others) and generates a “Fatal error: Call to undefined function add_settings_section()”.

    However, when using your instructions in the functions.php this doesn’t occur.

    One other really weird thing I noticed is that when the plugin “Plugin Notes” is used, and the instructions for the separator are added via functions.php all the plugins now have a duplicate “add plugin note” link and plugins that have a note have duplicate links. This resolves itself when the separator code is removed from functions.php. Mind you… the Plugin Notes plugin is super old (while still functional) but I just thought I would mention it in case someone else uses your code (awesome) with that plugin and runs into the same problem.

    :)

    1. Hey Amber – which plugin are you referring to? The separate code mentioned in this post doesn’t make a call to add_settings_section.

      A couple of other tutorial articles do, but they are theme based and they should work as it’s part of the WordPress API.

      Let me know – I’m more than happy to try to resolve this (and thanks for bringing it to my attention :)).

      1. Hi Tom,

        So far I have found that if I try to activate the Github plugin, it causes a fatal error when the following plugins are active: WooCommerce, Code Snippets, and WP-Invoice.

        So for example if I have a site that has WooCommerce activated, and I download/install/activate the plugin from github the error is:

        Fatal error: Call to undefined function add_settings_section() in /wp-content/plugins/woocommerce/admin/woocommerce-admin-init.php on line 806

        Line 806 in that file is as follows:
        add_settings_section( ‘woocommerce-permalink’, __( ‘Product permalink base’, ‘woocommerce’ ), ‘woocommerce_permalink_settings’, ‘permalink’ );

        When it’s used with the Code Snippets plugin if I attempt to activate the github separator plugin it generates:

        Fatal error: Call to undefined function add_settings_section() in /wp-content/plugins/code-snippet-library/classes/class-code-snippet-library-settings.php on line 36

        Line 36 in that file is as follows: add_settings_section( ‘snippet_theme_settings’ , __( ‘Modify your code snippet theme’ , ‘code_snippet’ ) , array( &$this , ‘main_settings’ ) , ‘code_snippet_settings’ );

        When none of the above plugins are active then I can successfully activate the Separator plugin without errors.

        If I use the code in this article directly in my theme’s function.php file instead of the github separator plugin (while all of the above plugins are active) there are no error’s at all.

        1. Hi Tom,

          This just gets weirder and weirder for me… so down the rabbit hole we go!

          I think in the github plugin the line that’s causing the fatal error issues is:

          add_action( ‘init’, array( $this, ‘set_admin_menu_separator’ ) );

          If I change that line to:

          add_action( ‘admin_menu’, array( $this, ‘set_admin_menu_separator’ ) );

          It activates without any fatal errors, but doesn’t appear to work properly on default installs. Making the above change adds the separator underneath “Settings” (even though it could be set for 79 or 6, etc.)

          One thing that I have noticed while trying this script on both fresh WP installs (no plugins and using Twenty Twelve) and a few other installs (have multiple plugins activated) is that on default sites the code to put into the fuctions.php in this article doesn’t quite work.

          It will create the separator but not at position 79, it pushes it under “Settings” and if you change it to say position 6; it doesn’t move (same as the plugin after modifying init to admin_menu).

          The only way that I got it to work (by default) was to change the code to:

          add_action( ‘init’, ‘set_admin_menu_separator’ );

          function set_admin_menu_separator() {
          $position = 79;
          global $menu;
          $menu[$position] = array(
          0 => ”,
          1 => ‘read’,
          2 => ‘separator’ . $position,
          3 => ”,
          4 => ‘wp-menu-separator’
          );
          }

          In the above using admin_menu or admin_init in replace of init did not work. Using admin_menu results in no separator showing and admin_init loads the separator under “Settings”.

          Here’s where it gets odd, or so I think.

          The only time that the code that you listed in your article works and adds the separator in the correct position is when WooCommerce is activated.

          If WooCommerce is active it will place the Separator using the code in this tutorial at wherever it’s been defined with 0 issues. If I activate WooCommerce and the Code Snippets (mentioned in my previous post), the separator continues to function.

          If I deactivate WooCommerce and keep only Code Snippets active, the separator only shows in position 6 (which is the position being used by Code Snippets); any position past 6 and it goes back to loading under “Settings”.

          The only way I have been able to get the code snippet in your tutorial to function in all positions I have defined is if WooCommerce has been activated first (I’m pretty sure I saw something in their code once about it modifying the menu but I could be wrong).

          If I again deactivate WooCommerce (no plugins) I’m back to the separator not loading in any point other than after “Settings” unless I use the code posted earlier in this comment.

          Sorry for writing such a book about this!

    1. Hey Amber,

      A lot of these questions are going beyond support for some sample code ;).

      Ultimately, the purpose of the post was to show how you could introduce a separator into the WordPress admin. If the code above is causing conflicts with an existing plugin, I’m all for changing up which hook it uses (and I like having it documented here if it works for you), but I can’t really offer support via comments for this in one off cases.

      That said, the best way to create multiple separators would be this:

      Create a function that accepts a number as an argument
      Call the function however many times you need passing a number into it
      Generalize the code in the function to using the incoming argument as where to place the separator

      If the function is made public, you can do so from outside of the class itself; otherwise, you could call it whenever the plugin is instantiated (so call it within the constructor).

      Perhaps something like this:

      $this->add_separator( 2 )

      You could even generalize the function to accept an array of arguments then write a loop that iterators through each of the arguments adding the separator.

      So then that’d look like this:

      $this->add_separator( array( 2, 4, 6, 8 ) );

      And the core function would be:

      function add_separator( $arr_locations ) {
      foreach( $arr_locations as $location ) {
      // Add your separator here
      }
      }

      There’s a variety of ways to do it :).

  7. Hi Tom,

    You put me on the right track. I was able to achieve this via the following modified code (not sure if it’s the best way but it works):

    /* Rocket Boosters Engaged! Set Coordinates */
    function set_admin_menu_separator( ) {
    $positions = array ( 'sep1'=>200, 'sep2'=>201, 'sep3'=>202, 'sep4'=>203 );
    foreach($positions as $position) {
    do_action( 'admin_init', $position );
    }
    }
    add_action('admin_menu', 'set_admin_menu_separator');

    One thing I thought I’d mention is that using this code in conjunction with some themes + bbPress causes weird “”Fatal error: Allowed memory size exhausted” errors on any menu items that are custom post types from the admin dashboard (i.e. posts). This is occurring using either my above revised code or the original article code.

    Not exactly sure why it happens but thought it would be worth mentioning. One way I have found around it (if changing the theme isn’t an option) is to increase the WP Memory Limit above 64MB and it seems to resolve the issue.

  8. Awesome post. I am looking this type of post for long time. However i think if is better to use code without plugins because this is a very simple code for add separator to wordpress menu.

  9. Hi, I tried your plugin using wp 3.5 and it did upload ok but when I activate it I get a internal server error. Then i’m unable to access my admin area anymore.

    I did delete the plugin and tried it again. Same issue.

    Please advise?

    1. Since this is a free plugin, I’ll try to take a look at it when I have a chance, but it’s not currently on my list of priorities.

      The best advice I can give right now is to look at the line on which the error appears to be reporting and then attempt to fix it from there.

  10. After installing your plugin i get this message on my website.

    Fatal error: Call to undefined function add_settings_section() in /home/sushitoday.nl/www/wordpress/wp-content/themes/verona/option-tree/includes/ot-settings-api.php on line 410

    What can i do about this?

    I can’t do anything on my website at the moment.

    Greetings Kevin

  11. Thank you.

    Another very simple way:

    In the menu editor, below “CSS classe (optional)”, add “mySeparator” or whatever you want beginning with “my” in order to be sure you wont mess up the CSS.

    Go to Appearance –> Editor –> select a CSS, the one you prefer (basically, style.css).

    Add this line where you want inside the body of the file, beginning a new line just after a closing curly brace:

    .mySeparator {border-bottom: black solid 1px;}

    Or change size, color and mode (dotted…..)

    You’re done

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.