3 Ways to Add Notification Counter Bubbles to WordPress Admin Menu
Here is what I mean:

Any of the described methods below have something one in common – we are going to modify the text of a menu item with a <span>
element using one of the classes below:
<span class="awaiting-mod">2</span>
– I recommend to use this one,<span class="update-plugins">11</span>
Method 1. While Creating a Custom Admin Page
$notification_count = 2; // here we get the count somehow in the code
add_menu_page(
'Tickets',
$notification_count ? sprintf( 'Tickets <span class="awaiting-mod">%d</span>', $notification_count ) : 'Tickets',
'manage_options',
'tickets_page_slug',
'tickets_page_handler'
);
Using sprint_f()
is not necessary unless you are going to translate it.
This method also works great with the functions:
add_submenu_page()
add_options_page()
– This function and the below ones add a submenu page under an appropriate parent menu item, for example this function itself adds it under Settings, for any of the below functions it should be clear from the function name.add_management_page()
add_dashboard_page()
add_posts_page()
add_media_page()
add_users_page()
Let me show you an example just to make the things clear.
$notification_count = 52;
add_management_page(
'My Tool Page',
'My Tool <span class="awaiting-mod">' . $notification_count . '</span>',
'manage_options',
'my_tool',
'my_tool_callback'
);
Nice and easy 🙃

Method 2. While Registering a Custom Post Type
It is all about register_post_type()
function and its $labels
array.
$count = 2;
register_post_type(
'support',
array(
'labels' => array(
'name' => 'Tickets',
// .. more labels could be here ...
'all_items' => $count ? 'All tickets <span class="awaiting-mod">' . $count . '</span>' : 'All tickets',
),
'public' => true,
/// .. more function arguments ...
)
);

Please note, that using this method you can only add a notification bubble to a child “All tickets” menu item, if you want to add it to a parent “Tickets”, you have to use the 3rd method which is described below. Using menu_name
label doesn’t bring you any effect, because it escapes HTML.
Method 3. Using global $menu
Fist of all I recommend you to look how global menu array looks like. If your website is not in production, try this: $global $menu; print_r( $menu ); exit;
, do it inside admin_menu
hook.

It is easy to change any menu item lable from this array, isn’t it? Like this:
add_action( 'admin_menu', function() {
global $menu;
$count = 541;
$menu[2][0] = 'Dashboard <span class="awaiting-mod">' . $count. '</span>';
});
Stop! Unless it is your own website, please do not do it! I know it is so easy but menu items can change their position depending on plugins installed and custom post types registered. We just can not know what position could have a certain menu element.
Instead let’s take a look at wp_list_filter()
function which allows us to find out the menu position easily from the array.
Example for Media:
add_action( 'admin_menu', function() {
global $menu;
$count = 5;
$menu_item = wp_list_filter(
$menu,
array( 2 => 'upload.php' ) // 2 is the position of an array item which contains URL, it will always be 2!
);
if ( ! empty( $menu_item ) ) {
$menu_item_position = key( $menu_item ); // get the array key (position) of the element
$menu[ $menu_item_position ][0] .= ' <span class="awaiting-mod">' . $count . '</span>';
}
});
And the result:

The third method doesn’t allows us to add notification counter bubbles to submenu items.
Now you know all the three methods and depending on your task you can use the one which fits you the most.

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
:wink::relieved::taxi::wink::relieved::taxi::wink::relieved::taxi:
thank u!
Thanks!
Awesome, we just integrated it in my Project. :)
Great content! Super high-quality! Keep it up! :)
Thank you!
It fits very well to my project.
Br,
Thank you, that helped me a lot !