WordPress add_menu_page Example: Register a Top-Level Admin Screen
Use add_menu_page() to register a clean top-level plugin screen with the right capability model and callback checks.
Published
May 4, 2026
Reading Time
2 min read
Updated
May 4, 2026

Implementation Notes
Extension points, code paths, and implementation choices that should survive contact with production.
Best For
WordPress developers, agencies, and technical teams building custom plugin or theme functionality with cleaner operational defaults.
Primary Topics
Editorial Focus
Build Pattern: Extension points, code paths, and implementation choices that should survive contact with production. Updated on May 4, 2026.
Full Report
Last reviewed: May 4, 2026
A plugin admin screen should be easy to find without being sloppy about access control. add_menu_page() is the core helper for registering a top-level WordPress admin page, but production code still needs a clean menu slug, the right capability, and a second permission check inside the render callback.
This guide shows how to add a top-level admin page safely and keep the implementation readable when the screen eventually grows into a real plugin surface.
Register the page on the admin menu hook
<?php
add_action( 'admin_menu', 'vulnwp_register_reports_page' );
function vulnwp_register_reports_page() {
add_menu_page(
__( 'Security Reports', 'vulnwp' ),
__( 'Security Reports', 'vulnwp' ),
'manage_options',
'vulnwp-security-reports',
'vulnwp_render_reports_page',
'dashicons-shield',
58
);
}
The documentation is explicit here: the page should be added through admin_menu, not too early in bootstrap.
Check the capability again inside the callback
<?php
function vulnwp_render_reports_page() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You are not allowed to view this page.', 'vulnwp' ) );
}
echo '<div class="wrap">';
echo '<h1>' . esc_html__( 'Security Reports', 'vulnwp' ) . '</h1>';
echo '<p>' . esc_html__( 'Daily review data and plugin health signals.', 'vulnwp' ) . '</p>';
echo '</div>';
}
The menu capability controls visibility, but the callback should still defend itself. If someone guesses the page slug directly, the server-side callback remains the final authority.
Use a stable menu slug and keep it unique
The code reference notes that the menu slug should be compatible with sanitize_key(). That means lowercase alphanumeric characters, dashes, and underscores are the safe path. Changing the slug casually later can break internal links, screen targeting, and admin navigation habits.
Choose the capability based on the task, not habit
manage_options is common for plugin settings, but it is not automatically the right choice for every admin screen. Reporting, editorial review, and custom operational dashboards may justify a narrower capability model.
Common mistakes
- Hooking too early.
admin_menuis the right registration point for menu screens. - Using a role name instead of a capability. Capabilities are the permission contract WordPress uses.
- Skipping the callback check. Visibility and authorization are not the same thing.
- Letting the screen become a mixed-purpose dump. A top-level page should represent one coherent function.
Production checklist
- Register the page on
admin_menu. - Use a unique and stable slug.
- Pick the narrowest capability that matches the screen’s purpose.
- Perform a second
current_user_can()check inside the page callback. - Keep the page focused so the menu hierarchy remains understandable.
Related reading
If the screen belongs under an existing parent instead of the top level, continue with the add_submenu_page guide. If the screen saves plugin options, pair it with the Settings API article.


