Type something to search...
How to Create a Custom User Role in WordPress?

How to Create a Custom User Role in WordPress?

The five default WordPress roles are built for a generic blog, and plenty of sites outgrow them fast. A store that needs someone who can manage orders but never touch a blog post, or an agency dashboard where a client should see reports but nothing else, both need a role that doesn't exist out of the box. Building one is a handful of lines of code, using the same add_role() function every plugin that registers its own roles relies on.

This guide walks through registering a custom role, giving it a precise set of capabilities, cloning an existing role as a starting point, and using map_meta_cap to handle permissions that depend on who owns a specific piece of content — not just what type of content it is.

The add_role() Function

add_role() takes three arguments: a role slug, a display name, and an array of capabilities:

function tw_register_custom_roles() {
    add_role(
        'store_manager',
        __( 'Store Manager' ),
        [
            'read'               => true,
            'upload_files'       => true,
            'manage_shop_orders' => true,
            'edit_shop_orders'   => true,
        ]
    );
}
register_activation_hook( __FILE__, 'tw_register_custom_roles' );

A few details that matter:

  • Capability names are just strings you define. manage_shop_orders isn't a built-in WordPress capability — it's a custom one this code invents, and it only means something once your own current_user_can( 'manage_shop_orders' ) checks (or a plugin's) look for it.
  • read should almost always be included. Without it, the user can't even access their own dashboard profile page.
  • Run this on plugin activation, not on every page load. add_role() writes to the wp_user_roles option in the database every time it's called. Wrapping it in register_activation_hook() (rather than hooking it to init) means it runs exactly once, when the plugin is activated, instead of on every single request.

Removing a Custom Role Cleanly

Whatever registers a role should also be responsible for removing it, typically on deactivation or uninstall, so you don't leave orphaned roles behind if the plugin is ever removed:

function tw_remove_custom_roles() {
    remove_role( 'store_manager' );
}
register_deactivation_hook( __FILE__, 'tw_remove_custom_roles' );

Keep in mind that remove_role() only removes the role definition — any users currently assigned that role are left with a role slug that no longer exists, which WordPress treats as having no capabilities at all. Reassign affected users to a fallback role (usually subscriber) before removing a role that's actively in use.

Cloning an Existing Role as a Starting Point

Often the role you want is "Editor, plus one or two extra capabilities" rather than something built entirely from scratch. Since WP_Role::capabilities is just an array, you can merge it into a new role:

function tw_register_senior_editor_role() {
    $editor = get_role( 'editor' );

    if ( ! $editor ) {
        return;
    }

    add_role(
        'senior_editor',
        __( 'Senior Editor' ),
        array_merge(
            $editor->capabilities,
            [ 'edit_theme_options' => true ]
        )
    );
}
register_activation_hook( __FILE__, 'tw_register_senior_editor_role' );

This creates an independent role — later changes to the built-in Editor role won't affect Senior Editor, and vice versa, since add_role() copies the capability array at the moment it runs rather than linking the two roles together.

Handling Ownership with map_meta_cap

A flat capability like manage_shop_orders works fine when a permission doesn't depend on which specific object is involved. But plenty of real permissions do: an Author can edit their own posts but not someone else's, and the same logic often applies to custom roles — a Store Manager might be allowed to edit any order, while a regular staff member should only edit orders they personally created.

This is what map_meta_cap is for. It intercepts a "meta capability" check (one tied to a specific object, passed as $args[0]) and maps it down to the actual primitive capabilities WordPress should check instead:

add_filter( 'map_meta_cap', 'tw_map_shop_order_caps', 10, 4 );

function tw_map_shop_order_caps( $caps, $cap, $user_id, $args ) {
    if ( 'edit_shop_order' !== $cap || empty( $args[0] ) ) {
        return $caps;
    }

    $order = get_post( $args[0] );

    if ( ! $order ) {
        return $caps;
    }

    // Store managers can edit any order.
    if ( user_can( $user_id, 'manage_shop_orders' ) ) {
        return [ 'manage_shop_orders' ];
    }

    // Everyone else can only edit orders they created.
    return (int) $order->post_author === (int) $user_id
        ? [ 'exist' ]
        : [ 'do_not_allow' ];
}

Now current_user_can( 'edit_shop_order', $order_id ) correctly checks ownership for regular staff while giving Store Managers unrestricted access, using the exact same call site everywhere else in your code — you never need to write if ( $user->has_role('store_manager') || $order->post_author == $user_id ) scattered across templates, since the filter centralizes that logic in one place.

do_not_allow is a real, reserved capability name that no role should ever be granted — returning it from map_meta_cap is the standard way to explicitly deny a check.

Assigning the Custom Role to Users

Once registered, a custom role shows up in the Role dropdown on the Users > All Users > Edit User screen exactly like a built-in one, and can be set programmatically the same way as covered in managing user roles and permissions:

$user = get_user_by( 'email', 'jane@example.com' );
$user->set_role( 'store_manager' );

Testing a Custom Role Before Rolling It Out

Before assigning a new role to real users, verify it behaves as intended:

  • Create a test account and assign it the new role.
  • Log in as that account (a private browser window is the easiest way to stay logged in as both accounts at once) and confirm the dashboard shows exactly the menus and options you expect — nothing more, nothing less.
  • Install a free auditing plugin like User Role Editor temporarily, which lists every capability a role has in a checkbox grid, making it easy to spot a capability you forgot to include or accidentally left in from a cloned role.

Frequently Asked Questions (FAQ) About Creating Custom User Roles in WordPress

Yes, using get_role( 'editor' )->add_cap() or remove_cap(), covered in more detail in managing user roles and permissions. Creating a separate custom role is usually the better choice when the change is significant, since it keeps the original built-in role available and unmodified for other purposes.

A small, dedicated plugin (or a must-use plugin) is generally better than a theme's functions.php, since a role tied to your theme disappears if the site ever switches themes, while a plugin-registered role survives independently of the active theme.

No — and you shouldn't. add_role() writes to the database every time it runs, so it belongs in register_activation_hook(), which fires once when the plugin is activated, not in a hook like init that runs on every request.

A regular capability (edit_posts) is a flat yes/no check independent of any specific object. A meta capability (edit_post, with a specific post ID) depends on context — who owns the object, what state it's in — and gets translated into real primitive capabilities through the map_meta_cap filter before WordPress checks anything.

They're left with a role slug WordPress no longer recognizes, which is treated as having zero capabilities — effectively locking them out of most of the dashboard. Reassign any users on a role before removing it, typically by looping through get_users( [ 'role' => 'store_manager' ] ) and calling set_role() on each.

Capabilities alone control actions (editing, publishing, deleting), not which specific pieces of content a role can view on the front end. For that, you'd pair a custom role with content-gating logic, which is exactly what restricting content access in WordPress covers.

Plugins like User Role Editor and Members provide a full visual interface for creating roles and toggling capabilities, and are a reasonable choice if you need this occasionally rather than as part of a plugin you're actively developing. For anything version-controlled and deployed across environments, code is more reliable than a setting stored only in one site's database.

Conclusion

Custom roles are the right tool whenever the built-in five don't cleanly describe what someone on your site actually needs to do. add_role() and remove_role() handle registration and cleanup, cloning an existing role's capability array is often the fastest starting point, and map_meta_cap is what to reach for the moment a permission needs to depend on ownership rather than being a flat yes or no.

Keep role registration in a plugin's activation hook rather than running on every request, and always plan for what happens to existing users if a role is ever removed. Once you're comfortable with this pattern, it scales cleanly from a single extra capability to an entire custom permission system built around exactly how your site works.

Tags :
Share :

Related Posts

Effortlessly Crafting Compelling WordPress Pages

Effortlessly Crafting Compelling WordPress Pages

As a website owner or content creator, having the ability to seamlessly add new pages to your WordPress site is crucial. Whether you're introducing a

Continue Reading
High Traffic Tips for WordPress Mastery 🚥

High Traffic Tips for WordPress Mastery 🚥

In our digital age, where online visibility is paramount, ensuring your WordPress site can handle surging traffic is crucial. Just like a finely-tune

Continue Reading
How Do I Change the WordPress Login URL?

How Do I Change the WordPress Login URL?

By default, every WordPress site's login page lives at the same predictable address: yoursite.com/wp-login.php (which also happens to redirect from

Continue Reading