WordPress at Your Fingertips
rgbcode is looking for WordPress developers.

add_menu_page()WP 1.5.0

Add a top-level menu page to the Dashboard (next to Posts, Pages, Users etc).

It is used to create a dashboard menu item and attach a function to this item that will be responsible for its page.

If you wish to add a submenu item, use add_submenu_page().

If you get "You do not have sufficient permissions to access this page." error when you're visiting a menu page, that means that the function is used too early.

You should fire the function on admin_menu action.


  1. This function takes a ‘capability’ (see Roles and Capabilities) which will be used to determine whether or not to show the menu item in the menu. This capability uses for check access to the menu page as well.

  2. If you use the Settings API to save data and you need this functionality work for users with lower then administrator role, you need to modify the permissions via the option_page_capability_{$option_group} hook. $option_group here — is the same as $menu_slug.

    An example of how to allow users with editor role to change settings:

    add_action( 'admin_menu', 'register_my_page' );
    add_filter( 'option_page_capability_my_page_slug', 'my_page_capability' );
    // Add a new menu item for editors
    function register_my_page(){
    	add_menu_page( 'My Page Title', 'My Page', 'edit_others_posts', 'my_page_slug', 'my_page_function', plugins_url( 'myplugin/images/icon.png' ), 6 );
    // Change capabilities
    function my_page_capability( $capability ) {
    	return 'edit_others_posts';

No Hooks.


String. The resulting page's hook_suffix.


add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
$page_title(string) (required)
The text to be displayed in the <title> tags of the page when the menu is selected.
$menu_title(string) (required)
The text to be used for the menu.
$capability(string) (required)
The capability required for this menu to be displayed to the user. List of roles and capabilities.
$menu_slug(string) (required)

The slug name to refer to this menu. Should be unique and only include lowercase alphanumeric, dashes, and underscores characters.

If the $function parameter is not specified, you need to specify the name of the PHP file (relative to the plugin directory) in this parameter. Specified file will be responsible for the output of the page content of this menu item.

You can also specify an arbitrary link (URL). In this case, you will go to the URL by clicking the menu item.


The function to be called to output the content for this page.

This is an optional parameter and if it is not specified, WordPress expects the PHP file from $menu_slug parameter generate the admin menu page.

There are two variants to specify this parameter:

  1. If the function is a class method:
    array( $this, 'function_name' )
    or static:
    array( __CLASS__, 'function_name' ).
  2. Function name as a string.

Default: ''


The URL to the icon to be used for this menu item.

  • Image ― if you want to use a custom icon, you can specify a URL to the icon. For example, plugin_dir_url( __FILE__ ) .'plugin-icon.png'. The size of the icon should be 20x20 pixels or less.

  • dashicons ― since WP 3.8, you can use special dashicons icons. Just select one from the list and use its name. For example, dashicons-dashboard.

  • base64 ― since WP 3.8, you can specify base64 encoded SVG image: base64-encoded SVG. In this case this parameter should begin with data:image/svg+xml;base64,.... In this case, the icon will be specified as a background-image.

    IMPORTANT: since WP 6.0 only SVG formats can be specified this way (png, jpg will not work).

  • none ― to leave div.wp-menu-image empty, to add icon later from CSS.

  • '' — By default, when used an empty string '', dashicons-dashboard from the dashicons is used, and the CSS class menu-icon-generic is added.

Default: ''


The number that determines the menu item position. The larger the number, the lower the menu item will be.

Attention! If two items use the same digit-position, one of the menu items can be overwritten and only one of the two items will be shown. To avoid such conflict, you can use float values instead of integers: 63.3 instead of 63. Use quotes: "63.3".

By default, the menu item will be added to the end of the list.

Menu structure:

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
99 – Separator

Default: null



#1 Theme settings

This example shows how to add a theme settings page to the main menu of the WordPress dashboard.

add_action('admin_menu', function(){
	add_menu_page( 'Additional site settings', 'Additional settings', 'manage_options', 'site-options', 'add_my_setting', '', 4 );
} );

function add_my_setting(){
	<div class="wrap">
		<h2><?php echo get_admin_page_title() ?></h2>

		// settings_errors() won't work on other than options pages
		if( get_current_screen()->parent_base !== 'options-general' )

		<form action="options.php" method="POST">
				settings_fields("opt_group");     // hidden protection fields
				do_settings_sections("opt_page"); // settings section


#2 Add a menu item for administrator

Add a menu item that only administrators can see:

1st variant (only for plugins):

add_action( 'admin_menu', 'register_my_custom_menu_page' );
function register_my_custom_menu_page(){
		'custom menu title', 'custom menu', 'manage_options', 'myplugin/myplugin-admin.php', '', plugins_url( 'myplugin/images/icon.png' ), 6

In this case the menu page code should be in this file wp-content/plugins/myplugin/myplugin-admin.php:

	echo "Some page code.";

2nd variant:

add_action( 'admin_menu', 'register_my_custom_menu_page' );
function register_my_custom_menu_page(){
		'custom menu title', 'custom menu', 'manage_options', 'custompage', 'my_custom_menu_page', plugins_url( 'myplugin/images/icon.png' ), 6

function my_custom_menu_page(){
	echo "Some page code.";

#3 Add a menu item if it has not been added

If you need to make sure that a menu item has not yet been added, before adding it, you can use global variable $admin_page_hooks:

global $admin_page_hooks;

if( isset($admin_page_hooks['menu_slug']) ){
	add_submenu_page( ... );
else {
	add_menu_page( ..., ..., ..., 'menu_slug' );
	add_submenu_page( ... );

#4 Check if a menu or submenu item exists

This functions checks if a menu or submenu item exists by its slug.

 * Finds the specified menu or submenu item item in the dashboard.
 * Use after 'admin_menu' hook.
 * Usage example: if( is_admin_menu_item_exists('options-general.php') ){  }
 * @param  string  $handle        Menu slug. Specified in 4 parameter of add_menu_page() or add_submenu_page()
 * @param  boolean [$sub = false] Is menu or submenu ID specified?
 * @return boolean Whether a menu item exists
function is_admin_menu_item_exists( $handle, $sub = false ){

	if( !is_admin() || (defined('DOING_AJAX') && DOING_AJAX) )
		return false;

	global $menu, $submenu;

	$check_menu = $sub ? $submenu : $menu;

	if( empty($check_menu) )
		return false;

	foreach( $check_menu as $k => $item ){
		if( $sub ){
			foreach( $item as $sm ){
			  if( $handle == $sm[2] )
				return true;
		elseif( $handle == $item[2] )
			return true;

	return false;

#5 Menu item with a link to edit the post

Suppose we want to add an arbitrary link to the menu: /wp-admin/post.php?post=270&action=edit:

add_action( 'admin_menu', 'change_menu' );

function change_menu() {

	$post_id  = 393;
	$url_edit = get_edit_post_link( $post_id );

	if ( $url_edit ) {
		add_menu_page( '', 'My article', 'edit_posts', $url_edit, '', '', 5 );


  • Global. Array. $menu
  • Global. Array. $admin_page_hooks
  • Global. Array. $_registered_pages
  • Global. Array. $_parent_pages


Since 1.5.0 Introduced.

add_menu_page() code WP 6.4.1

function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $icon_url = '', $position = null ) {
	global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;

	$menu_slug = plugin_basename( $menu_slug );

	$admin_page_hooks[ $menu_slug ] = sanitize_title( $menu_title );

	$hookname = get_plugin_page_hookname( $menu_slug, '' );

	if ( ! empty( $callback ) && ! empty( $hookname ) && current_user_can( $capability ) ) {
		add_action( $hookname, $callback );

	if ( empty( $icon_url ) ) {
		$icon_url   = 'dashicons-admin-generic';
		$icon_class = 'menu-icon-generic ';
	} else {
		$icon_url   = set_url_scheme( $icon_url );
		$icon_class = '';

	$new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url );

	if ( null !== $position && ! is_numeric( $position ) ) {
				/* translators: %s: add_menu_page() */
				__( 'The seventh parameter passed to %s should be numeric representing menu position.' ),
		$position = null;

	if ( null === $position || ! is_numeric( $position ) ) {
		$menu[] = $new_menu;
	} elseif ( isset( $menu[ (string) $position ] ) ) {
		$collision_avoider = base_convert( substr( md5( $menu_slug . $menu_title ), -4 ), 16, 10 ) * 0.00001;
		$position          = (string) ( $position + $collision_avoider );
		$menu[ $position ] = $new_menu;
	} else {
		 * Cast menu position to a string.
		 * This allows for floats to be passed as the position. PHP will normally cast a float to an
		 * integer value, this ensures the float retains its mantissa (positive fractional part).
		 * A string containing an integer value, eg "10", is treated as a numeric index.
		$position          = (string) $position;
		$menu[ $position ] = $new_menu;

	$_registered_pages[ $hookname ] = true;

	// No parent as top level.
	$_parent_pages[ $menu_slug ] = false;

	return $hookname;
vladlu 100vlad.lu
Editors: Kama 211
1 comment
    Log In