Admin Menus in WordPress

Share this article

The administration system in WordPress works well enough at first glance. The built-in menu functions take care of a lot of the issues such as adding and removing menu items, but you may want to learn more about how WordPress handles its menus internally to leverage more control. The example below will show how you can implement a plugin which enables you to reorder the menu items in the order you wish while explaining the nuances of the WordPress menu system.

How WordPress orders Menu Items

To best show how the position of a menu item can cause problems, let’s create a small plugin to demonstrate the way in which WordPress orders menu items when adding them. Create a directory in the plugins directory, such as <root-installation>/wp-content/plugins/adminpagearranger. Then, create a file called init.php in the directory and use the WordPress plugin descriptor syntax to identify the plugin to the system. The WordPress documentation advises that adding admin pages should be done with the admin_menu action hook, so add this directly under the descriptor. The easiest way to add a menu item to the administration panel is with the add_menu_page() function which takes seven arguments:
  • text that is displayed in the title of the page
  • the text of the menu item which is used in displaying the menu item
  • the capability that you define to allow access to this menu item
  • a menu slug identifier which identifies this menu from any other in the system
  • a function which renders the page in the browser
  • the path to an icon which is used to display with the menu, and
  • the position that the new menu item should appear in relation to other menu items
Place the following code in init.php:
<?php
/*
Plugin Name: Admin Page Arranger
Plugin URI: http://www.example.com/adminpagearranger
Description: A plugin to adminster admin pages
Version: 0.1
Author: Tim Smith
Author URI: http://www.example.com/
License: GPL2
*/

add_action("admin_menu", "addMenu");

function addMenu() {
    add_menu_page("My New Menu", "My New Menu", "edit_posts",
        "mynewmenu", "displayPage", null, 1);
}
The code will generate a new page link which sits at the top of the menu structure, just above the Dashboard: But, consider the following implementation of addMenu() instead:
<?php
function addMenu() {
    add_menu_page("My New Menu", "My New Menu", "edit_posts",
        "mynewmenu", "displayPage", null, 2);
}
Just by changing the position from 1 to 2 replaces the Dashboard entry with your new menu! Why? It’s all got to do with how WordPress stores its menu configuration. WordPress uses a global array called $menu to store the menu configuration, and each key in the array represents the position in the menu. A higher position in the array indicates a higher position in the menu. However, WordPress populates this array on start up with default values. For example:
Array
(
    [2] => Array
        (
            [0] => Dashboard
            [1] => read
            [2] => index.php
            [3] => 
            [4] => menu-top menu-top-first menu-icon-dashboard
            [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
            [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
        )
...
)
Each entry in the array is in fact another array providing the details of each menu item. So when the call to add a menu item at position 2 is executed, it actually replaces whatever may have been at position 2 prior. In this case, this is the dashboard’s information. When you’re adding a new menu item, be mindful of the position you specify. Note also that the arguments supplied to the add_menu_page() function have now been used to populate the global array.

Specifying a Custom Menu Order

Now that you know how WordPress stores its menu configuration, you can use this to control where menu items appear in the $menu array and thus change the order in which the menus appear. WordPress offers two filters which can be used to change the menu: menu_order and custom_menu_order. custom_menu_order requires a Boolean value to be returned which indicates whether a custom menu should be used. In effect, the returned value from custom_menu_order dictates whether the menu_order filter is applied. The return value of the custom_menu_order filter should return an array detailing the slugs of the menus you want to appear in their order. You can use the following table to select the slugs which relate to the standard menu items.
<?php
add_filter("custom_menu_order", "allowMenuStructure");
add_filter("menu_order", "loadMenuStructure");

function allowMenuStructure() {
    return true;
}

function loadMenuStructure() {
    return array("index.php", "tools.php");
}
You don’t have to specify all of the slugs to be displayed when returning the array from custom_menu_order; WordPress will automatically populate the rest of the menu with the remaining menu items in the order they would normally be placed.

Creating a Custom Menu Manager

We can leverage the custom_menu_order filter to populate the menu in the desired order by storing an array in WordPress to be returned when this filter is applied. You do this by way of update_option which either creates or updates a value which you can call later with get_option(). WordPress stores the data in the database table wp_options for later retrieval. If the option has not yet been sent, or is absent for whatever reason, you can create it based on the default menu structure. The first thing you should do is add your own menu item so that you can administer the menu order. The returned value from add_menu_page()
is a “hook suffix” which you can append to a number of action hooks which are specific to the page. The one we are interested in here is load-page_hook where page_hook should be substituted with the hook suffix. This hook is an ideal place to process form submissions as it will only occur when this page is loaded. Change the addMenu() function we used earlier to test the menu functionality as follows:
<?php
function addMenu() {
    $page = add_menu_page("Admin Manager", "Admin Manager",
        "manage_options", "adminmanager", "displayAdminManager",
        null, null);
    add_action("load-" . $page, "handleMenu");
}
This short piece of code obliges us define two more functions – displayAdminManager() which is responsible for displaying the page and handleMenu() which we will use to process any changes we make to the menu order. Before you define these, however, it makes sense to define a function which loads a menu structure obtained from the wp_options table or sets up a default. This function will be used in a couple of places.
<?php
function getMenuStructure() {
    $structure = get_option("menustructure");
    if (!$structure) {
        global $menu;
        $newMenu = array();
        foreach ($menu as $menuItem) {
            $newMenu[] = $menuItem[2];
        }
        return $newMenu;
    }
    else {
        return $structure;
    }
}
getMenuStructure() uses get_option() to attempt to pull a menu structure from the database. If it isn’t available, then it constructs an array to pass back. This function will be used to actually populate your new menu structure and also to list the menu items when displaying the plugin so you can move the items up and down the structure. You may find that some plugin developers place their own menu items wherever they want. This can sometimes be inconvenient as you will find that the normal menu items will be separated from where they should be. Plugin developers are dissuaded from doing this in general, but you’ll find occasionally someone feels their plugin is more important than the others and so they add it to the top of the menu system. If you want to get around this and make sure that all other additional menu items are placed below everything else, you can change the function to:
<?php
function getMenuStructure() {
    $structure = get_option("menustructure");
    if (!$structure) {
        return array("index.php", "separator1", "edit.php",
            "upload.php", "link-manager.php",
            "edit.php?post_type=page",
            "edit-comments.php", "separator2", "themes.php",
            "plugins.php", "users.php", "tools.php",
            "options-general.php", "separator-last");
    }
    else {
        return $structure;
    }
}
This enforces that the default WordPress menu structure is implemented and any other items will be displayed after it. In the hard-coded structure, noticed I also used separator entries. The separators should be identified uniquely with the word “separator” followed by an integer, a the last one should be identified as “separator-last”. So now you have a generic function for obtaining a menu structure and you can look at defining the function to display the menu manager so that the menu can be manipulated. You’ll need to include links so that when clicked, they reload the page whereupon the load-page_hook action is run.
<?php
function displayAdminManager() {
    $menu = getMenuStructure();
    foreach($menu as $key => $menuitem) {
        $uplink = "admin.php?page=adminmanager&menuitem=" . $key . "&direction=up";
        $downlink = "admin.php?page=adminmanager&menuitem=" . $key . "&direction=down";
        echo $menuitem . ' - <a href="' . $uplink . '">Up</a> <a href="' . $downlink . '">Down</a><br>';
    }
}
Note that we call the page itself; the URL for this page is /admin.php?page=adminmanager
. The adminmanager identifier is the slug that was set when you added the page via add_menu_page() which was the fourth argument. There are also a couple of arguments detailing the direction the menu item should be moved (i.e. up or down) and which menu item will be affected. So, now you have a means by which we can pass arguments to process via load-page_hook which was defined as handleMenu(). The key to this is that we swap the menu items over depending on the direction that was specified and when this is done, you use update_option() to store your new menu structure. Then the viewer is redirected back to “this” page. This ensures the new menu is loaded and implemented. It is also a good idea to ensure that menu items cannot be moved off the structure by testing whether the menu item to be manipulated is the first or last in the array.
<?php
function handleMenu() {

    if (isset($_GET["menuitem"]) && isset($_GET["direction"])) {
        $structure = get_option("menustructure");
        $numberItems = count($structure);
        $lastIndex = $numberItems - 1;

        if (!$structure) {
            $menu = getMenuStructure();
        }
        else {
            $menu = $structure;
        }

        // if the menu item is to be moved UP the menu
        if ($_GET["direction"] == "down" && $_GET["menuitem"] != $lastIndex) {
            // move the menu up
            $currentIndex = $_GET["menuitem"];
            $nextIndex = $currentIndex + 1;

            $currentMenuItem = $menu[$currentIndex];
            $nextMenuItem = $menu[$nextIndex];

            $menu[$currentIndex] = $nextMenuItem;
            $menu[$nextIndex] = $currentMenuItem;

            update_option("menustructure", $menu);
        }
        //if the menu item is to be moved DOWN the menu
        elseif ($_GET["direction"] == "up" && $_GET["menuitem"] != 0)
        {
            //move the menu down
            $currentIndex = $_GET["menuitem"];
            $previousIndex = $currentIndex - 1;

            $currentMenuItem = $menu[$currentIndex];
            $previousMenuItem = $menu[$previousIndex];

            $menu[$currentIndex] = $previousMenuItem;
            $menu[$previousIndex] = $currentMenuItem;

            update_option("menustructure", $menu);
        }

        wp_redirect("admin.php?page=adminmanager");
    }
}
Redirection is necessary because of the order in which the actions take place. The load-page_hook action occurs after the custom_menu_order filter, so updating the “menustructure” option takes place but you won’t see the effect since the custom menu order has already been loaded. A redirect will ensure the new menu structure is implemented correctly by essentially “refreshing” the page and the WordPress life cycle.

Summary

This tutorial taught you how the internal admin menus are structured in WordPress. You built a simple plugin to implement a system whereby you can change the order of the menu items. We used WordPress’s internal options to store and retrieve this data and a custom filter to ensure our custom menu order is implemented. James Threw / Shutterstock

Frequently Asked Questions about Admin Menus in WordPress

How can I add a new menu to my WordPress admin panel?

Adding a new menu to your WordPress admin panel is a straightforward process. First, you need to navigate to your WordPress dashboard. From there, go to ‘Appearance’ and then ‘Menus’. Click on ‘create a new menu’. You will be prompted to name your new menu. After naming it, click ‘Create Menu’. You can then add pages, posts, custom links, or categories to your menu by selecting them and clicking ‘Add to Menu’. Remember to save your changes.

How can I rearrange the items in my WordPress admin menu?

Rearranging items in your WordPress admin menu is simple. Go to ‘Appearance’ and then ‘Menus’ in your WordPress dashboard. Click on the menu you want to rearrange. You can then drag and drop the menu items to rearrange them. Remember to click ‘Save Menu’ after you’re done.

Can I add custom links to my WordPress admin menu?

Yes, you can add custom links to your WordPress admin menu. Navigate to ‘Appearance’ and then ‘Menus’ in your WordPress dashboard. Click on ‘Custom Links’, enter the URL and link text, and then click ‘Add to Menu’. Don’t forget to save your changes.

How can I remove a menu item from my WordPress admin menu?

To remove a menu item, go to ‘Appearance’ and then ‘Menus’ in your WordPress dashboard. Click on the menu item you want to remove and then click ‘Remove’. Remember to save your changes.

Can I create a dropdown menu in WordPress?

Yes, you can create a dropdown menu in WordPress. In the ‘Menus’ section, drag the menu item slightly to the right of the item above it. This will create a sub-item, which appears as a dropdown menu on your website.

How can I rename a menu item in WordPress?

To rename a menu item, navigate to ‘Appearance’ and then ‘Menus’ in your WordPress dashboard. Click on the menu item you want to rename. You can then edit the ‘Navigation Label’. Remember to save your changes.

Can I add a menu to a specific page in WordPress?

Yes, you can add a menu to a specific page in WordPress. First, create a new menu. Then, go to ‘Appearance’ and then ‘Widgets’. Drag the ‘Navigation Menu’ widget to the desired widget area and select the menu you created.

How can I make a menu item open in a new tab?

To make a menu item open in a new tab, go to ‘Appearance’ and then ‘Menus’ in your WordPress dashboard. Click on ‘Screen Options’ at the top right of the screen and check the box for ‘Link Target’. You can then select ‘Open link in a new tab’ for any menu item.

Can I add a menu to my footer in WordPress?

Yes, you can add a menu to your footer in WordPress. Go to ‘Appearance’ and then ‘Widgets’. Drag the ‘Navigation Menu’ widget to the ‘Footer’ widget area and select the menu you want to add.

How can I add a menu to my sidebar in WordPress?

To add a menu to your sidebar, navigate to ‘Appearance’ and then ‘Widgets’. Drag the ‘Navigation Menu’ widget to the ‘Sidebar’ widget area and select the menu you want to add.

Tim SmithTim Smith
View Author

Tim Smith is a freelance web designer and developer based in Harrogate, North Yorkshire in the UK. He started out programming the Spectrum 48k and has never looked back. When he's not boring his other half with the details of his latest project, Tim can be found with his beloved Korgs making music, reading a good book or in the pub.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week