Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use get_role() to retrieve an existing WordPress role, then call add_cap() or remove_cap() on that role. Run the change once during plugin activation or another setup lifecycle event, and enforce the permission where the protected operation occurs with current_user_can().

Roles and capabilities: the distinction

A WordPress role is a named bundle of permissions, such as Editor or Author. A capability is one specific permitted action— for example, editing posts or publishing posts. The WordPress Developer Resources handbook defines capabilities as what a role can and cannot do.

Changing a role changes the permissions of every user assigned that role. A custom capability has no effect by itself; your plugin, theme, custom post type, or admin page must check it before allowing the action.

Add a capability to an existing role

Retrieve the role by its slug and add the capability only if the role exists:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
$role = get_role( 'editor' );

if ( $role ) {
    $role->add_cap( 'manage_custom_reports' );
}

WP_Role::add_cap() grants the capability (its grant argument defaults to true). WordPress persists the updated role data in the site options, so the permission remains after the request ends and after a normal page load.

Granting or revoking explicitly

$role = get_role( 'editor' );

if ( $role ) {
    $role->add_cap( 'manage_custom_reports', true );
    $role->remove_cap( 'manage_custom_reports' );
}

Do not run the removal immediately after the addition in real code; the second call is shown only to illustrate the two methods. Use remove_cap() when the capability should no longer be present.

Remove a capability safely

Remove the capability from the role that currently carries it:

$role = get_role( 'editor' );

if ( $role ) {
    $role->remove_cap( 'manage_custom_reports' );
}

This affects the role, not just one user. If only one person should lose access, use a separate role or a user-specific capability design rather than changing a shared role.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run role changes once, not on every request

Role data is saved, so repeatedly calling add_cap() or remove_cap() during every request creates unnecessary option writes. Put setup in a plugin activation hook (or an equivalent controlled setup event), and undo it on deactivation when that matches the feature’s lifecycle.

function my_reports_activate() {
    $role = get_role( 'editor' );

    if ( $role ) {
        $role->add_cap( 'manage_custom_reports' );
    }
}
register_activation_hook( __FILE__, 'my_reports_activate' );

function my_reports_deactivate() {
    $role = get_role( 'editor' );

    if ( $role ) {
        $role->remove_cap( 'manage_custom_reports' );
    }
}
register_deactivation_hook( __FILE__, 'my_reports_deactivate' );

If the role may be created by another component, perform the mutation after that component has registered the role—for example, from an init callback with an appropriate priority. Guarding the result of get_role() prevents errors when the slug is unavailable.

When not to remove on deactivation

Deactivation is not always the same as uninstalling. If administrators expect permissions to remain while a plugin is temporarily disabled, leave the capability in place and remove it only in an explicit uninstall or migration routine.

Check the capability where the action happens

Granting a capability is only half of authorization. Check it immediately before displaying or performing the protected operation:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
if ( current_user_can( 'manage_custom_reports' ) ) {
    // Display the report tools or perform the operation.
}

Use the permission check in the server-side handler as well as in the interface. Hiding a button without protecting the handler does not prevent a direct request.

Use object-aware meta capabilities

For access to a particular object, pass its ID to a meta capability:

if ( current_user_can( 'edit_post', $post_id ) ) {
    // Edit this specific post.
}

WordPress maps meta capabilities such as edit_post to the user’s primitive capabilities according to the object and context. Prefer this object-aware check when the decision concerns one post or another individual resource.

Why not check the role name?

Checking roles directly in place of capabilities is only partially supported and is discouraged. Capability checks continue to work when administrators customize role names or assign equivalent permissions through another role.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Do not confuse capability changes with role changes

add_role() creates a role only when that role does not already exist. Calling it again does not update the capabilities of an existing role.

Operation Effect Use when
get_role() + add_cap() Adds one capability to an existing role and persists it. A role already exists and needs an additional permission.
get_role() + remove_cap() Removes one capability from an existing role and persists it. A role should no longer perform a specific action.
add_role() Creates a missing role; it does not revise an existing role. Defining a new role with an initial capability set.
Remove and re-add a role Rebuilds the role’s capability set, potentially affecting assigned users and custom changes. A deliberate bulk reset after verifying the expected state.

Removing a role is consequential. The WordPress handbook advises against removing Administrator or Super Admin. If removing Subscriber, update the default_role option first because Subscriber is WordPress’s default role unless your site has configured another default.

Multisite considerations

Roles and capabilities are site-scoped in a multisite network. Make role changes in the intended site context, and use a site-specific check when testing access against another site:

if ( current_user_can_for_blog( $blog_id, 'manage_custom_reports' ) ) {
    // The current user can perform this action on that site.
}

Do not assume a capability granted on one network site automatically grants it on every site.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Code or a dashboard plugin?

Approach Advantages Trade-offs
Code with get_role() and WP_Role Version-controlled, repeatable, reviewable, and easy to deploy consistently. Requires PHP access and careful lifecycle handling.
Role-management plugin Dashboard workflow for administrators who prefer not to edit code. Exact persistence, multisite behavior, and cleanup depend on the chosen plugin; no particular plugin is implied here.

Whichever method you use to grant a permission, enforce the authorization at the protected operation with a capability check.

Troubleshooting checklist

  • Nothing changed: confirm the role slug (for example, editor) and verify that get_role() returned an object.
  • The permission keeps returning: search for another setup routine or plugin that re-adds the capability.
  • The button is hidden but requests still work: add a server-side current_user_can() check to the handler.
  • A custom capability appears unused: locate the code path that checks it; declaring a capability alone does not create behavior.
  • Multisite results differ: check the current site and use current_user_can_for_blog() when evaluating another blog.
  • A role recreation caused surprises: review assigned users, custom capability changes, and the site’s default_role before removing or rebuilding the role.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.