Horizontal Submenus for WordPress Admin Plugins
How to Add Horizontal Submenus to Your Administration Plugin in WordPress 2.7+
With the launch of version 2.7 in December 2008, WordPress added several new features and changed its user interface (UI). I especially like the new automatic update feature and the new vertical sidebar is sleeker and more modern looking. However, I find navigating the menus on that new sidebar to be very inefficient when administration plugins add more selections to it. ![]()
Image #1: Plugin with sidebar menu
As an admin plugin author, I want my users to have easy access to my plugin menu and submenus. In WordPress 2.2 to 2.6, the built-in menu functions accomplished this with horizontal menus at the top of the page. In 2.7, however, the same menu functions now place the plugin menu (and submenus) near the bottom of the page, in the sidebar. This makes navigation more cumbersome because plugin menus are positioned outside the initial viewing area of the screen in a typical 1280×800 browser window. The user has to scroll down to select the plugin and, after the plugin is selected, scroll down again to navigate the plugin’s submenus. Worse, it not always obvious that there are submenus available for a plugin. You can see an example of this in image #1 where the green line indicates the end of the window area of a 1280×800 resolution browser window.
To workaround this problem in my own admin plugin, I inserted a new horizontal menu containing the submenu links at the top of each submenu page. ![]()
image #2: Plugin with horizontal menu To keep the look consistent with WordPress' new style, I positioned the menu inline with the “contextual help” button and also adopted it’s “look and feel”. This was a relatively simple task to do, once I located and copied the appropriate styles from WordPress' admin stylesheets. You can see an example of this new horizontal menu in the second image to the right.
Below are step-by-step instructions on how you can do this for your own “top-level” admin plugin with submenus. Please note that the rest of this article assumes prior knowledge of WordPress plugin writing and basic PHP, HTML and CSS skills.
- Step #1: Create the plugin menu and submenu pages
Use the WordPress functions,
add_menu_pageandadd_submenu_page, to create your plugin's menu and submenus. This maintains compatibility with earlier versions of WordPress (2.0+) and sets the “page” parameter for your submenu pages. This parameter is used on the admin url to link to your plugin pages and is needed for the submenu links in the horizontal menu in step #2 below.Your plugin menu/submenu code should look something like this:
<?php define('DEMOFILE',basename(__FILE__)); //give plugin a top-level WordPress admin menu with submenus function demo_menus() { // userlevel=8 restrict users to "Administrators" only add_menu_page('demofromhel', 'Demo from Hel', 8, DEMOFILE, 'demo_content'); add_submenu_page(DEMOFILE, 'SubMenu-1 Title', 'SubMenu-1', 8, DEMOFILE, 'demo_content'); add_submenu_page(DEMOFILE, 'SubMenu-2 Title', 'SubMenu-2', 8, 'demo-page2', 'demo_content'); add_submenu_page(DEMOFILE, 'SubMenu-3 Title', 'SubMenu-3', 8, 'demo-page3', 'demo_content'); }The constant, DEMOFILE, is your plugin filename and also the “page” parameter value for the default submenu (SubMenu-1) page.
- Step #2: Create a new menu as an “unordered” list of links to each submenu page
Inside your plugin's content function, create an unordered list (<ul>) containing links to each submenu page. This will be your plugin's horizontal submenu for WordPress 2.7. Insert this list just above the main “wrap” <div> in your document. Give the <ul> tag a unique id and give each <li> tag the same class name. The submenu page links should be listed in the reverse of their intended display order. This is necessary because of the
float:rightstyle attribute used to format the menu (see step #3 below). After the terminating </ul> tag, add an empty <div> with the inline style,style="clear:right;". This extra <div> fixes a css bug in IE7. Then, enclose the entire code within a PHP statement that tests for WordPress version 2.7 or higher. This “if” code block excludes the plugin horizontal submenu from earlier versions of WordPress where it would be redundant. This section of code looks like:function demo_content() { global $wpdb, $wp_version; if (version_compare($wp_version, '2.7', '>=')) { ?> <ul id="demo-menu"> <li class="demo-menu-link"><a href="?page=demo-page3">SubMenu-3</a></li> <li class="demo-menu-link"><a href="?page=demo-page2">SubMenu-2</a></li> <li class="demo-menu-link"><a href="?page=<?php echo DEMOFILE; ?>">SubMenu-1</a></li> </ul> <div style="clear:right;"></div> <?php } ?> <div class="wrap"> <div id="icon-plugins" class="icon32"></div> <h2>Demo from Hel</h2> <?php //output page content for each plugin submenu page if ($_GET['page'] == "demo-page3") { print '<p>Demo page 3 content</p>'; } elseif ($_GET['page'] == "demo-page2" ) { print '<p>Demo page 2 content</p>'; } else { //page 1 is default print '<p>Demo page 1 / default content</p>'; } ?> <hr/> </div> <?php }Of course, your plugin should output content for each submenu “page” of your plugin. As the above example shows, you can read PHP's
$_GET['page']parameter to identify individual pages and customize content accordingly. The content must be enclosed inside a “wrap” division (<div class="wrap">) in keeping with WordPress' admin style standard and for the “ul” menu to be formatted properly. Also, note that the variable,$wp_versionis a global WordPress variable and must be declared in the “GLOBAL” section of your content function. - Step #3: Style your new menu to display horizontally, like the “help” button
Next, in your plugin's css stylesheet file, insert the following css code to format the menus so that they are in-line with the “contextual help” button and they use the same background image. If you don’t have a separate css file for your plugin, just put these lines in a <style> tag in your plugin's “head” function or above the <ul> menu code in the “content” function.
#demo-menu { display: inline; position: relative; } #demo-menu a, #demo-menu a.link { text-decoration: none; z-index: 1; margin: 0 auto; padding: 0 6px 0 6px; height: 22px; line-height: 22px; font-size: 10px; background-repeat: no-repeat; background-position: right bottom; } .demo-menu-link { float: right; background: transparent url(images/screen-options-left.gif ) no-repeat 0 0; font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif; height: 22px; padding: 0; margin: 0 6px 0 0; text-decoration: none; text-align:center; }You can expand on these styles by adding “a:visited”, “a:hover” and “a:active” css attributes as well.
- Step #4: Integrate your plugin and menus into WordPress with admin “hooks”
Lastly, near the bottom of your plugin program, add the appropriate admin hooks to incorporate your plugin functions into WordPress. Use the “admin_menu” hook to integrate the menus and submenu pages into WordPress admin. Use the “admin_head” hook to integrate your plugin stylesheet (and javascripts, if any) into WordPress admin <head>.
//hook the plugin functions into WordPress admin add_action('admin_menu', 'demo_menus'); add_action('admin_head', 'demo_head');Note that the “demo_content” function is also “hooked” into WordPress via the “admin_menu” hook as each submenu page calls that function. Also, the “admin_head” hook is not needed unless you created a “demo_head” function containing the <style> tag for your css code or containing the css stylesheet link.
And you are DONE!
Here is the entire code in a single program:
<?php
global $wpdb, $wp_version;
define('DEMOFILE',basename(__FILE__));
//give plugin a top-level WordPress admin menu with submenus
function demo_menus() {
// userlevel=8 restrict users to "Administrators" only
add_menu_page('demofromhel', 'Demo from Hel', 8, DEMOFILE, 'demo_content');
add_submenu_page(DEMOFILE, 'SubMenu-1 Title', 'SubMenu-1', 8, DEMOFILE, 'demo_content');
add_submenu_page(DEMOFILE, 'SubMenu-2 Title', 'SubMenu-2', 8, 'demo-page2', 'demo_content');
add_submenu_page(DEMOFILE, 'SubMenu-3 Title', 'SubMenu-3', 8, 'demo-page3', 'demo_content');
}
function demo_head() {
global $wp_version;
if (version_compare($wp_version, '2.7', '>=')) { ?>
<style type="text/css">
#demo-menu {
display: inline;
position: relative;
}
#demo-menu a, #demo-menu a.link {
text-decoration: none;
z-index: 1;
margin: 0 auto;
padding: 0 6px 0 6px;
height: 22px;
line-height: 22px;
font-size: 10px;
background-repeat: no-repeat;
background-position: right bottom;
}
.demo-menu-link {
float: right;
background: transparent url(images/screen-options-left.gif ) no-repeat 0 0;
font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
height: 22px;
padding: 0;
margin: 0 6px 0 0;
text-decoration: none;
text-align:center;
}
</style>
<?php
}
}
function demo_content() {
global $wpdb, $wp_version;
if (version_compare($wp_version, '2.7', '>=')) { ?>
<ul id="demo-menu">
<li class="demo-menu-link"><a href="?page=demo-page3">SubMenu-3</a></li>
<li class="demo-menu-link"><a href="?page=demo-page2">SubMenu-2</a></li>
<li class="demo-menu-link"><a href="?page=<?php echo DEMOFILE; ?>">SubMenu-1</a></li>
</ul>
<div style="clear:right;"></div>
<?php } ?>
<div class="wrap">
<div id="icon-plugins" class="icon32"></div>
<h2>Demo from Hel</h2>
<?php //output page content for each plugin submenu page
if ($_GET['page'] == "demo-page3") {
print '<p>Demo page 3 content</p>';
} elseif ($_GET['page'] == "demo-page2" ) {
print '<p>Demo page 2 content</p>';
} else { //page 1 is default
print '<p>Demo page 1 / default content</p>';
} ?>
<hr/>
</div>
<?php }
//hook the plugin functions into WordPress admin
add_action('admin_menu', 'demo_menus');
add_action('admin_head', 'demo_head');
?>
I put this code into a functioning plugin that you can install, activate, and test for yourself. You can download it here. To install, unzip the archive, copy demo_from_hel.php to your plugin directory, and activate.
This horizontal menu code was tested successfully in IE 6, IE 7, FF 1.5-mac, FF 2, FF 3, Mozilla 1.7, Safari 1.3, Safari 2, Safari 3-Win, Chrome 1.0, and Camino 1.6. Feedback regarding other browsers would be appreciated.
For more information on integrating WordPress’ adminstration menus or submenus into your plugin, see the codex doc: Adding Administration Menus. To learn about writing plugins for WordPress, read the codex doc: Writing_a_Plugin.


Great plugin. Where exactly would the page content go?
Would one create a page for each sub menu and link to that.
How would a page be addressed?
Hi Mike,
Your answer is in the “add_menu_page” and “add_submenu_page” WordPress functions. The last two parameters for these functions are the page identifier and the function that displays the page content, respectively.
To “address” an individual plugin page, use the identifier that you created in “add_menu_page/add_submenu_page” (ex: DEMOFILE, demo-page2, demo-page3). That identifier becomes the “page” query argument on the URL for WordPress admin, ex:
http://myblog.com/wp-admin/admin.php?page=demo-page3.To show your page content when your plugin’s menu is selected, write a function with your page content in it, then enter that function name as the last parameter for “add_submenu_page()”. You can write different functions for each individual page or one function for all the pages like in my example (“demo_content”). For the latter, I read the
$_GET['page']parameter to customize content by page identifier.Thanks for coming back.
I have modified the code slightly to try and get 3 functions for each submenu andI have tried to follow your suggestions,but I get an error ‘cant redeclare author_menus()
If you have time could you comment on my code please
Many thanks
<?php /* Plugin Name: Authors-1 Plugin URI:http://www.techfromhel.com/2009/03/horizontal-submenus-wordpress Description: A WordPress administration plugin with top-level menu and a horizontal submenu for demonstration only Version: 0.1 Author: Helene D.http://www.techfromhel.com Author URI: Released under the GNU General Public License (GPL) http://www.gnu.org/licenses/gpl.txt */ global $wpdb, $wp_version; define('Authors-1',basename(__FILE__)); // create a top-level menu with submenu pages for wordpress admin function author_menus() { // userlevel=8 restrict users to "Administrators" only add_menu_page('Authors-1', 'Authors', 8, Authors-1, 'author_content'); add_submenu_page(Authors-1, 'SubMenu-1 Title', 'SubMenu-1', 8, '__FILE__', 'subMenu1'); add_submenu_page(Authors-1, 'SubMenu-2 Title', 'SubMenu-2', 8, '__FILE__', 'subMenu2'); add_submenu_page(Authors-1, 'SubMenu-3 Title', 'SubMenu-3', 8, '__FILE__', 'subMenu3'); } //output style and other code for document function author_head() { global $wp_version; if (version_compare($wp_version, '2.7', '>=')) { ?> #demo-menu { display: inline; position: relative; } #demo-menu a, #demo-menu a.link { text-decoration: none; z-index: 1; margin: 0 auto; padding: 0 6px 0 6px; height: 22px; line-height: 22px; font-size: 10px; background-repeat: no-repeat; background-position: right bottom; } .demo-menu-link { float: right; background: transparent url(images/screen-options-left.gif ) no-repeat 0 0; font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif; height: 22px; padding: 0; margin: 0 6px 0 0; text-decoration: none; text-align:center; <?php } } // output content for document function author_content() { global $wpdb, $wp_version; if (version_compare($wp_version, '2.7', '>=')) { ?> <a>subMenu3 <a href="?page=Authors-1.php">subMenu2</a> <a href="?page=Authors-1.php">subMenu1</a> Authors Additional Information These pages allow you to add additional information to support your book idea or synopsis. Got to your options page if you want publishers to view this additonal informationMike,
‘cannot redeclare’ is a common WordPress error that can occur when (a) a function name defined in your plugin is not unique, i.e. when 2 copies/versions of the same plugin is active or when 2 plugins have coincidental duplicate function names,
or when (b) a plugin is not coded as a php class, especially in WordPress 2.7+
One work-around for (b) is to move all ‘add_action’ hooks into a single function, then write a new ‘add_action’ to call this function using the ‘plugins_loaded’ hook, for example, in my demo plugin, the last 2 lines of code can be replaced by this:
I hope this helps.
FYI. To add code in comments without WordPress stripping the tags, enclose your code inside <code></code> tags and remove all comment notation (// and /*) and blank lines from the code.
Thanks for your reply. I found the cause of the prolem, but will remeber your solution.
Unfortunately i still cant get the sub menu to work.
Any help much appreciated.
My lataest effort is as follows:-
<?php /*Plugin Name: Writers Plugin URI:http://www.techfromhel.com/2009/03/horizontal-submenus-wordpress Description: A WordPress administration plugin with top-level menu and a horizontal submenu for demonstration only Version: 0.1 Author: Helene D.http: //www.techfromhel.com Author URI: Released under the GNU General Public License (GPL) http ://www.gnu.org/licenses/gpl.txt */ global $wpdb, $wp_version; define('Writers',basename(__FILE__)); //create a top-level menu with submenu pages for wordpress admin function author_menus() { // userlevel=8 restrict users to "Administrators" only add_menu_page('Writers', 'Authors', 8, 'Writers', 'author_content'); add_submenu_page(Writers, 'SubMenu-1 Title', 'SubMenu-1', 8, 'subMenu1', 'author_content'); add_submenu_page(Writers, 'SubMenu-2 Title', 'SubMenu-2', 8, 'subMenu2', 'author_content'); add_submenu_page(Writers, 'SubMenu-3 Title', 'SubMenu-3', 8, 'subMenu3', 'author_content'); } //output style and other code for document function author_head() { global $wp_version; if (version_compare($wp_version, '2.7', '>=')) { ?> #demo-menu { display: inline; position: relative; } #demo-menu a, #demo-menu a.link { text-decoration: none; z-index: 1; margin: 0 auto; padding: 0 6px 0 6px; height: 22px; line-height: 22px; font-size: 10px; background-repeat: no-repeat; background-position: right bottom; } .demo-menu-link { float: right; background: transparent url(images/screen-options-left.gif ) no-repeat 0 0; font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif; height: 22px; padding: 0; margin: 0 6px 0 0; text-decoration: none; text-align:center; } <?php } } //output content for document function author_content() { global $wpdb, $wp_version; if (version_compare($wp_version, '2.7', '>=')) { ?> <a href="?page=subMenu3">subMenu3</a> <a href="?page=subMenu2">subMenu2</a> <a href="?page=subMenu1">subMenu1</a> ... Authors Additional Information These pages allow you to add additional information to support your book idea or synopsis. Got to your options page if you want publishers to view this additonal informationMike,
Your code is incomplete. WordPress is still stripping it of tags. You need to describe your problem more specifically than “i still cant get the sub menu to work” and, if possible, do screen shots of your code and all error messages and add links to the images here.
However, I do see one error in your partial code: ‘add_menu_page’ should not have the 2nd ‘Writers’ quoted. That 4th argument is the constant that you defined in the line above.
Good luck and happy new year!!
Thanks Helene and a very happy new year to you too.
If I unquote the second writers the page doesnt load.error message=Cannot load Writers.
The specific problem is that when I select the horizontal tabs the submenus dont display.The functions are just set to echo the menu name at this point.
When i select any of the other tabs i get submenu 1 not the selected menu.
I have cut the code down a bit so hopefully you can see the relevant parts.
global $wpdb, $wp_version;
define('Writers',basename(__FILE__));
function author_menus() {
add_menu_page('Writers', 'Authors', 8, 'Writers', 'author_content');
add_submenu_page(Writers, 'SubMenu 1', 'SubMenu 1', 8, 'subMenu1', 'author_content');
add_submenu_page(Writers, 'SubMenu 2', 'SubMenu 2', 8, 'subMenu2', 'author_content');
add_submenu_page(Writers, 'SubMenu 3', 'SubMenu 3', 8, 'subMenu3', 'author_content');
}
function author_head() {
global $wp_version;
if (version_compare($wp_version, '2.7', '>=')) { ?>
=')) { ?>
subMenu3
subMenu2
subMenu1
Authors Additional Information
These pages allow you to add additional information to support your book idea or synopsis.
Got to your options page if you want publishers to view this additonal information
Sorry still stripping the code.
Can I email you with an attachment?
Mike, I sent you an email that you can reply to with the attachment.
Hi Mike! Great tutorial!! Thanks a lot! Tell me please can I use it in commercial plugins or themes?