WordPress includes a powerful roles and capabilities system that controls what each user can do on your site. The five default roles are: Subscriber (can only manage their own profile), Contributor (can write and submit posts for review), Author (can publish their own posts), Editor (can publish and manage all posts and pages), and Administrator (full control). Each role is a collection of capabilities — individual permissions like edit_posts, publish_pages, manage_options, or delete_users. Capabilities are checked with current_user_can( ‘capability_name’ ), which is the function you should use in every permission check rather than comparing role names (since a user can technically have multiple roles and a capability can belong to multiple roles). Adding a custom capability to a role is done via $role->add_cap( ‘my_custom_cap’ ) on the add_roles action — remember to run this only once (on plugin activation) because role data is stored in the database and repeated add_cap() calls every page load are wasteful. Creating a custom role is done with add_role(). This system is fundamental to any WordPress plugin that needs to restrict access to admin pages, custom post type management, or settings screens. Combine it with the role-based login redirect guide and the login activity logging guide for a complete user management toolkit.
Problem: You need to restrict certain admin pages, custom post type access, or settings to specific WordPress roles, or create a custom role with a specific set of permissions.
Solution: Add the following code to your functions.php file (run role modifications only once via activation hook):
// Check capabilities in templates and hooks
if ( current_user_can( 'manage_options' ) ) {
// only admins
}
if ( current_user_can( 'edit_posts' ) ) {
// contributors, authors, editors, admins
}
// Create a custom role (run once on plugin activation)
register_activation_hook( __FILE__, 'helloadmin_create_roles' );
function helloadmin_create_roles() {
add_role(
'store_manager',
__( 'Store Manager' ),
[
'read' => true,
'edit_posts' => true,
'manage_woocommerce' => true,
'view_woocommerce_reports' => true,
'helloadmin_view_stats' => true, // custom capability
]
);
}
// Add a capability to an existing role (run once on activation)
register_activation_hook( __FILE__, 'helloadmin_add_capabilities' );
function helloadmin_add_capabilities() {
$editor = get_role( 'editor' );
if ( $editor ) {
$editor->add_cap( 'helloadmin_view_stats' );
}
}
// Restrict an admin menu page to custom capability
add_action( 'admin_menu', 'helloadmin_add_stats_page' );
function helloadmin_add_stats_page() {
add_menu_page(
'Site Stats',
'Stats',
'helloadmin_view_stats', // required capability
'helloadmin-stats',
'helloadmin_render_stats_page',
'dashicons-chart-bar',
25
);
}
function helloadmin_render_stats_page() {
if ( ! current_user_can( 'helloadmin_view_stats' ) ) {
wp_die( __( 'You do not have permission to view this page.' ) );
}
echo '<div class="wrap"><h1>' . esc_html__( 'Site Stats' ) . '</h1></div>';
}
// Remove a role (cleanup on plugin uninstall)
// remove_role( 'store_manager' );
NOTE: Never hard-code role name checks like if ( $user->roles[0] === ‘editor’ ). Always use current_user_can( ‘capability’ ) instead — this is more flexible (a user with a custom role that has the same capability will pass the check), more readable, and consistent with how WordPress core and plugins handle permissions. Also, on multisite, site administrators are not super-admins; check is_super_admin() separately if you need network-level permission gating.