Scriptbaker
SCRIPTBAKERAI & Software Engineering
Engineering

Add custom menu item for WP subscribers panel

[code language=”php”] if (is_admin()) { add_action(‘admin_menu’, ‘my_menu’); } function my_menu() { add_menu_page(&#

· 5 min read

Introduction

WordPress provides a powerful admin menu API that lets plugin and theme developers add their own pages to the WordPress dashboard sidebar. One frequently overlooked capability is restricting those menu items to specific user roles — such as subscribers. By default, subscribers are the most limited user role in WordPress; they can only manage their own profile. But with add_menu_page() and a capability check, you can expose a custom admin page specifically for them. In this tutorial we will cover everything from the basic code snippet to access control, custom capability mapping, and organising submenu items under your new top-level menu entry.

Understanding WordPress User Roles and Capabilities

WordPress ships with five default roles: Administrator, Editor, Author, Contributor, and Subscriber. Each role is defined by a set of capabilities — boolean flags that grant or deny specific actions. The subscriber role has a single capability by default: read. Every other role has read plus additional capabilities.

When you register a menu page, the third argument to add_menu_page() is the minimum capability a user must hold to see that menu item. Passing 'read' means any logged-in user with at least subscriber access will see the page. Passing 'edit_posts' would hide it from subscribers but show it to authors and above.

This capability-based system is the foundation of everything we are about to build.

The Core Code: add_menu_page for Subscribers

Here is the complete, working code to add a custom top-level menu item visible to subscribers:

if ( is_admin() ) {
    add_action( 'admin_menu', 'sb_register_subscriber_menu' );
}

function sb_register_subscriber_menu() {
    add_menu_page(
        'My Subscriber Page',      // Page title (used in <title> tag)
        'My Panel',               // Menu label shown in the sidebar
        'read',                   // Minimum capability required
        'sb-subscriber-panel',    // Unique menu slug
        'sb_subscriber_page_cb',  // Callback function to render the page
        'dashicons-id-alt',       // Dashicon icon
        80                        // Menu position
    );
}

function sb_subscriber_page_cb() {
    if ( ! current_user_can( 'read' ) ) {
        wp_die( __( 'You do not have permission to view this page.' ) );
    }
    echo '<div class="wrap">';
    echo '<h1>' . esc_html( get_admin_page_title() ) . '</h1>';
    echo '<p>Welcome to your subscriber panel. Here you can manage your preferences.</p>';
    echo '</div>';
}

Let us break down each argument to add_menu_page():

  • Page title — the text used in the browser's title bar when this page is active.
  • Menu title — the label displayed in the admin sidebar.
  • Capability'read' ensures all logged-in users including subscribers can see it.
  • Menu slug — a unique string identifier for this menu item; used in URLs and as a hook suffix.
  • Callback function — the PHP function that outputs the page's HTML.
  • Icon — a Dashicons class string (see the full list at developer.wordpress.org/resource/dashicons).
  • Position — a numeric value controlling where in the sidebar the item appears.

Adding the Code to Your Plugin or functions.php

You can place this code in two locations depending on whether you are building a plugin or customising a theme:

Option A — Inside a plugin file: Add the code directly to your main plugin PHP file or a dedicated admin file included from it. Wrapping in is_admin() is optional here since admin_menu only fires in the admin context, but it is a good defensive habit.

// my-plugin/my-plugin.php
if ( is_admin() ) {
    require_once plugin_dir_path( __FILE__ ) . 'admin/subscriber-menu.php';
}

Option B — Inside functions.php: If you are adding this to a theme, paste the code into wp-content/themes/your-theme/functions.php. Be aware that theme-based admin functionality will disappear if the theme is switched — plugins are a more robust home for admin UI code.

Adding Submenus Under Your Custom Menu

Once you have a top-level menu entry, you can group related pages beneath it using add_submenu_page(). The first argument must match the slug you used for the parent menu:

function sb_register_subscriber_menu() {
    add_menu_page(
        'My Subscriber Page', 'My Panel', 'read',
        'sb-subscriber-panel', 'sb_subscriber_page_cb', 'dashicons-id-alt', 80
    );

    add_submenu_page(
        'sb-subscriber-panel',         // Parent slug
        'My Preferences',              // Page title
        'Preferences',                 // Menu label
        'read',                        // Capability
        'sb-subscriber-preferences',  // Slug
        'sb_preferences_page_cb'       // Callback
    );

    add_submenu_page(
        'sb-subscriber-panel',
        'My Downloads',
        'Downloads',
        'read',
        'sb-subscriber-downloads',
        'sb_downloads_page_cb'
    );
}

WordPress automatically creates the first submenu item as a duplicate of the parent. If you want the top-level entry to have a different label than the first submenu item, register a submenu with the same slug as the parent but a different title — WordPress will use it as the first child entry.

Restricting Pages to Only Subscribers (Excluding Admins)

Sometimes you want a page that subscribers can see but administrators cannot — for example a "Subscriber Perks" page that should not clutter the admin view. There is no built-in capability for this, but you can implement it easily with a role check inside the callback:

function sb_subscriber_page_cb() {
    $user = wp_get_current_user();

    // Only show to subscribers, not admins or editors
    if ( ! in_array( 'subscriber', (array) $user->roles ) ) {
        wp_die( 'This page is for subscribers only.' );
    }

    echo '<div class="wrap"><h1>Subscriber Perks</h1>';
    echo '<p>Thank you for being a subscriber!</p></div>';
}

Note that the menu item will still appear for all users with 'read' capability — this is a WordPress limitation. The role check inside the callback is what enforces the restriction; it just shows an error to non-subscribers who navigate directly to the URL rather than hiding the link.

To also hide the menu link from other roles, hook into admin_menu with a conditional:

add_action( 'admin_menu', function() {
    $user = wp_get_current_user();
    if ( in_array( 'subscriber', (array) $user->roles ) ) {
        add_menu_page(
            'Subscriber Perks', 'My Perks', 'read',
            'sb-perks', 'sb_perks_cb', 'dashicons-awards', 85
        );
    }
});

Adding Custom Capabilities to the Subscriber Role

If you want finer-grained control, you can add a custom capability to the subscriber role and use that as the menu's required capability. This way you can grant or revoke access per-user without touching the role itself:

// Run once on plugin activation
function sb_add_subscriber_caps() {
    $role = get_role( 'subscriber' );
    if ( $role ) {
        $role->add_cap( 'access_subscriber_panel' );
    }
}
register_activation_hook( __FILE__, 'sb_add_subscriber_caps' );

// Then use the custom capability in your menu
add_menu_page(
    'Subscriber Panel', 'My Panel', 'access_subscriber_panel',
    'sb-subscriber-panel', 'sb_subscriber_page_cb', 'dashicons-id-alt', 80
);

Remember to remove the capability on plugin deactivation to keep the database clean:

function sb_remove_subscriber_caps() {
    $role = get_role( 'subscriber' );
    if ( $role ) {
        $role->remove_cap( 'access_subscriber_panel' );
    }
}
register_deactivation_hook( __FILE__, 'sb_remove_subscriber_caps' );

Styling and Enqueueing Scripts for Your Admin Page

To add CSS or JavaScript only on your custom admin page, hook into admin_enqueue_scripts and check the $hook parameter. WordPress passes the current page hook suffix to this action:

add_action( 'admin_enqueue_scripts', 'sb_subscriber_panel_assets' );

function sb_subscriber_panel_assets( $hook ) {
    // The hook suffix is 'toplevel_page_' + your menu slug
    if ( 'toplevel_page_sb-subscriber-panel' !== $hook ) {
        return;
    }
    wp_enqueue_style(
        'sb-subscriber-panel',
        plugin_dir_url( __FILE__ ) . 'css/subscriber-panel.css',
        array(), '1.0.0'
    );
    wp_enqueue_script(
        'sb-subscriber-panel',
        plugin_dir_url( __FILE__ ) . 'js/subscriber-panel.js',
        array( 'jquery' ), '1.0.0', true
    );
}

For submenu pages the hook suffix format is my-panel_page_my-submenu-slug. You can always find the exact hook by temporarily adding echo $hook; inside admin_enqueue_scripts.

Summary

Adding a custom menu item for WordPress subscribers is a three-step process: hook into admin_menu, call add_menu_page() with the 'read' capability, and implement a callback function that renders your page HTML. From that foundation you can add submenus, restrict access to specific roles only, create custom capabilities, and enqueue page-specific assets — all without touching WordPress core files. Whether you are building a membership plugin, a subscriber dashboard, or a lightweight front-end alternative, the WordPress admin menu API gives you everything you need.

S

Scriptbaker Editorial Team

The Scriptbaker editorial team comprises engineers, AI specialists, and digital strategists based in Dubai and Rawalpindi. We write about software development, artificial intelligence, and digital transformation to help organisations build better products. Learn more about us →