
How to Customize the WordPress Admin Dashboard for Clients?
You hand over a finished WordPress site, and the next morning the client sends a screenshot of the dashboard with three questions: what is Site Health, why are there so many menus, and is it safe to click Tools? A default WordPress admin is built for people who run WordPress, not for a bakery owner who only needs to publish menu updates and swap a homepage banner. Every unused menu, news widget, and update notice is a chance for confusion or a support ticket.
The fix is not a page-builder skin or a "white label" plugin with 200 options. WordPress already gives you the tools: roles and capabilities decide what a user can do, and a handful of hooks decide what they see. This article covers building a client-friendly admin in one small plugin: creating a role with the right capabilities, cleaning up the admin menu, replacing default dashboard widgets with a useful one, trimming the toolbar, renaming confusing labels, branding the footer and colors, and adding admin CSS, plus the security line between hiding something and actually restricting it.
Start with the Right Mindset: Restrict, Then Tidy
There are two different jobs in admin customization, and mixing them up causes most of the problems:
| Job | Tool | Effect |
|---|---|---|
| Restrict what a user can do | Roles and capabilities | WordPress refuses the action, even if the user types the URL directly |
| Tidy what a user sees | remove_menu_page, remove_meta_box, toolbar and CSS hooks | Removes clutter, but the screen may still be reachable by URL |
Removing the Tools menu with remove_menu_page does not stop anyone from visiting /wp-admin/tools.php. If the user has the capability, the page loads. So the rule is simple: decide permissions with capabilities first, then use visual hooks to remove whatever is left over.
That is also why the client should almost never be an Administrator. An administrator can install plugins, edit theme files, change users, and break the site. Most clients need something closer to the Editor role. If you need a refresher on what each built-in role can do, see how to set up and manage user roles in WordPress.
Where to Put the Code
Admin customizations belong in a plugin, not in the theme's functions.php. If the client ever switches themes, the admin should not suddenly change. The cleanest option is a must-use plugin: a PHP file in wp-content/mu-plugins/ that loads automatically and cannot be deactivated from the Plugins screen.
- Create the folder
wp-content/mu-plugins/if it does not exist. - Create a file named
client-admin.phpinside it. - Add the plugin header below and save.
<?php
// wp-content/mu-plugins/client-admin.php
/**
* Plugin Name: Client Admin Customizations
* Description: Simplifies the WordPress admin for client users.
* Author: Web Solution Master
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
Every snippet in this article goes into that file below the header. If you prefer a regular plugin or a snippets plugin, the code works the same way; the trade-offs are covered in how to add custom code snippets to WordPress safely.
Step 1: Create a Client Role
A dedicated role is better than tweaking the built-in Editor role. It keeps WordPress defaults intact, and you can adjust the client's permissions without affecting anyone else.
The example below copies every Editor capability, then adds edit_theme_options so the client can manage menus and widgets, and list_users so they can see who has access.
// wp-content/mu-plugins/client-admin.php
const WSM_CLIENT_ROLE = 'site_manager';
const WSM_CLIENT_ROLE_VERSION = 2;
add_action( 'init', 'wsm_register_client_role' );
function wsm_register_client_role() {
// Only rebuild the role when the version changes, not on every request.
if ( (int) get_option( 'wsm_client_role_version' ) === WSM_CLIENT_ROLE_VERSION ) {
return;
}
$editor = get_role( 'editor' );
if ( ! $editor ) {
return;
}
$caps = $editor->capabilities;
$caps['edit_theme_options'] = true; // Appearance > Menus, Widgets, Site Editor.
$caps['list_users'] = true; // Read-only view of the Users screen.
remove_role( WSM_CLIENT_ROLE );
add_role( WSM_CLIENT_ROLE, 'Site Manager', $caps );
update_option( 'wsm_client_role_version', WSM_CLIENT_ROLE_VERSION );
}
Roles are stored in the database, so add_role only needs to run once. The version option makes the role rebuild only when you change the capabilities and bump the number.
Then assign it:
- Go to Users → All Users.
- Click the client's username to edit the profile.
- Change Role to Site Manager.
- Click Update User.
A few capability decisions worth making deliberately:
edit_theme_optionsgrants access to Menus, Widgets, the Customizer, and the Site Editor. On a block theme, that includes editing templates and global styles. If the client should only edit content, leave it out.unfiltered_htmlis part of the Editor role on single sites. It allows scripts in content. Remove it withunset( $caps['unfiltered_html'] )if the client should not be able to paste embed code with JavaScript.manage_optionsis the gateway to Settings. Clients rarely need it, and most of the cleanup below keys off it.
Also lock down file editing for everyone, including administrators, by adding this line to wp-config.php above the "That's all, stop editing!" comment:
// wp-config.php
define( 'DISALLOW_FILE_EDIT', true );
That removes the theme and plugin file editors entirely, which is a real security improvement, not a cosmetic one.
Step 2: Clean Up the Admin Menu
With permissions settled, remove menu items the client does not use. The admin_menu hook runs after WordPress and plugins build the menu, so use a late priority to catch items added by plugins.
// wp-content/mu-plugins/client-admin.php
add_action( 'admin_menu', 'wsm_simplify_admin_menu', 999 );
function wsm_simplify_admin_menu() {
// Leave the full menu for real administrators.
if ( current_user_can( 'manage_options' ) ) {
return;
}
remove_menu_page( 'edit-comments.php' ); // Comments (site has comments disabled).
remove_menu_page( 'tools.php' ); // Tools.
// Keep Appearance, but hide submenus the client does not need.
remove_submenu_page( 'themes.php', 'themes.php' ); // Theme switcher.
}
remove_menu_page takes the menu slug, which is the file name or page= value from the menu link. To find the slug for a plugin's menu, hover the menu item and read the URL in the browser status bar. For admin.php?page=wpseo_dashboard, the slug is wpseo_dashboard.
Rename Labels That Confuse Clients
Many clients publish "News" or "Updates," not "Posts." Renaming the built-in post type labels changes the menu, buttons, and screen titles in one place:
// wp-content/mu-plugins/client-admin.php
add_filter( 'post_type_labels_post', 'wsm_rename_posts_to_news' );
function wsm_rename_posts_to_news( $labels ) {
$labels->name = 'News';
$labels->singular_name = 'News Item';
$labels->menu_name = 'News';
$labels->name_admin_bar = 'News Item';
$labels->add_new_item = 'Add News Item';
$labels->edit_item = 'Edit News Item';
$labels->all_items = 'All News';
$labels->search_items = 'Search News';
$labels->not_found = 'No news found.';
$labels->not_found_in_trash = 'No news found in Trash.';
return $labels;
}
The post_type_labels_{$post_type} filter is the supported way to do this. It avoids editing the global $menu array, which breaks easily.
Step 3: Replace the Default Dashboard Widgets
The default dashboard shows WordPress events and news, Quick Draft, Activity, At a Glance, and Site Health. For a client, most of that is noise. Remove the defaults on the wp_dashboard_setup hook and add one widget that actually helps:
// wp-content/mu-plugins/client-admin.php
add_action( 'wp_dashboard_setup', 'wsm_client_dashboard_widgets', 99 );
function wsm_client_dashboard_widgets() {
if ( ! current_user_can( 'manage_options' ) ) {
remove_meta_box( 'dashboard_primary', 'dashboard', 'side' ); // WordPress Events and News.
remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' ); // Quick Draft.
remove_meta_box( 'dashboard_site_health', 'dashboard', 'normal' ); // Site Health Status.
remove_meta_box( 'dashboard_activity', 'dashboard', 'normal' ); // Activity.
remove_meta_box( 'dashboard_right_now', 'dashboard', 'normal' ); // At a Glance.
}
wp_add_dashboard_widget(
'wsm_client_help',
'Welcome to your website',
'wsm_render_client_help_widget'
);
}
function wsm_render_client_help_widget() {
$support_email = 'support@example.com';
?>
<p>Here is what you can do from this dashboard:</p>
<ul>
<li><a href="<?php echo esc_url( admin_url( 'post-new.php' ) ); ?>">Publish a news item</a></li>
<li><a href="<?php echo esc_url( admin_url( 'edit.php?post_type=page' ) ); ?>">Edit a page</a></li>
<li><a href="<?php echo esc_url( admin_url( 'upload.php' ) ); ?>">Upload images and documents</a></li>
</ul>
<p>
Need help? Email
<a href="<?php echo esc_url( 'mailto:' . $support_email ); ?>"><?php echo esc_html( $support_email ); ?></a>.
</p>
<?php
}
And remove the large Welcome panel at the top for client users:
// wp-content/mu-plugins/client-admin.php
add_action( 'admin_init', 'wsm_remove_welcome_panel' );
function wsm_remove_welcome_panel() {
if ( ! current_user_can( 'manage_options' ) ) {
remove_action( 'welcome_panel', 'wp_welcome_panel' );
}
}
Plugins add their own dashboard widgets too. To find a widget's ID, open Screen Options on the dashboard; each checkbox corresponds to a widget, and the ID appears in the HTML as the widget's id attribute when you inspect it in DevTools. Pass that ID and the widget's context (normal, side, or column3) to remove_meta_box.
Clients can also hide widgets themselves from Screen Options, but those preferences are per user and are easily undone. Code-level removal keeps the dashboard consistent for every client account.
Step 4: Trim the Admin Toolbar
The toolbar at the top of every screen has a WordPress logo menu, comment and update counters, and a "New" menu. Use the admin_bar_menu hook with a late priority to remove nodes and add your own:
// wp-content/mu-plugins/client-admin.php
add_action( 'admin_bar_menu', 'wsm_customize_toolbar', 999 );
function wsm_customize_toolbar( WP_Admin_Bar $wp_admin_bar ) {
$wp_admin_bar->remove_node( 'wp-logo' ); // WordPress logo and links.
if ( ! current_user_can( 'manage_options' ) ) {
$wp_admin_bar->remove_node( 'comments' );
$wp_admin_bar->remove_node( 'updates' );
$wp_admin_bar->remove_node( 'new-user' );
}
$wp_admin_bar->add_node(
array(
'id' => 'wsm-support',
'title' => 'Get Support',
'href' => 'mailto:support@example.com',
'meta' => array( 'class' => 'wsm-support-link' ),
)
);
}
The same hook runs on the front end when a logged-in user views the site, so the toolbar stays consistent everywhere. For a fuller tour of the toolbar and its nodes, see what is the purpose of the WordPress admin toolbar.
Step 5: Hide Update Notices from Clients
Users without update_core already cannot run updates, but WordPress still shows a "WordPress X.Y is available! Please notify the site administrator" banner to them. If you manage updates under a maintenance plan, that banner just generates worried emails.
// wp-content/mu-plugins/client-admin.php
add_action( 'admin_head', 'wsm_hide_update_nag_for_clients', 1 );
function wsm_hide_update_nag_for_clients() {
if ( ! current_user_can( 'update_core' ) ) {
remove_action( 'admin_notices', 'update_nag', 3 );
remove_action( 'network_admin_notices', 'update_nag', 3 );
}
}
The priority of 3 must match the priority WordPress used when it registered update_nag, or remove_action silently does nothing. Do not hide update notices from administrators; if no one sees them, updates do not happen.
Step 6: Brand the Footer, Login Logo Link, and Colors
Small branding touches make the admin feel like part of the client's site, not a generic tool.
Footer Text
// wp-content/mu-plugins/client-admin.php
add_filter( 'admin_footer_text', 'wsm_admin_footer_text' );
function wsm_admin_footer_text() {
return 'Website built and maintained by <a href="https://websolutionmaster.com">Web Solution Master</a>.';
}
add_filter( 'update_footer', 'wsm_hide_version_for_clients', 11 );
function wsm_hide_version_for_clients( $content ) {
return current_user_can( 'update_core' ) ? $content : '';
}
Login Logo Link
By default, the logo on the login screen links to WordPress.org. Point it at the client's site:
// wp-content/mu-plugins/client-admin.php
add_filter( 'login_headerurl', fn() => home_url( '/' ) );
add_filter( 'login_headertext', fn() => get_bloginfo( 'name' ) );
Replacing the logo image and restyling the whole login form is a bigger topic, covered in how to create a custom login page for a WordPress website.
A Custom Admin Color Scheme
WordPress ships with several admin color schemes under Users → Profile → Admin Color Scheme. You can register one in the client's brand colors and make it the default:
// wp-content/mu-plugins/client-admin.php
add_action( 'admin_init', 'wsm_register_admin_color_scheme' );
function wsm_register_admin_color_scheme() {
wp_admin_css_color(
'wsm-brand',
'Brand',
plugins_url( 'client-admin/brand-colors.css', __FILE__ ),
array( '#0a48ac', '#1d2327', '#7ed957', '#ffffff' )
);
}
add_filter( 'get_user_option_admin_color', 'wsm_default_admin_color' );
function wsm_default_admin_color( $color ) {
// Only apply when the user has not picked a scheme themselves.
return $color ? $color : 'wsm-brand';
}
The four colors in the array are only the preview swatches on the profile screen. The actual styling comes from the stylesheet, which you place at wp-content/mu-plugins/client-admin/brand-colors.css. The easiest way to build one is to copy an existing scheme from wp-admin/css/colors/ in the WordPress source and change its variables. plugins_url() with __FILE__ resolves correctly inside mu-plugins, so the URL works without hard-coding paths.
Step 7: Add Admin CSS for Small Fixes
Some clutter has no hook, such as a plugin's promotional banner. A small admin stylesheet handles it. Load it with admin_enqueue_scripts rather than printing a style tag:
// wp-content/mu-plugins/client-admin.php
add_action( 'admin_enqueue_scripts', 'wsm_client_admin_styles' );
function wsm_client_admin_styles( $hook_suffix ) {
if ( current_user_can( 'manage_options' ) ) {
return;
}
wp_enqueue_style(
'wsm-client-admin',
plugins_url( 'client-admin/admin.css', __FILE__ ),
array(),
'1.0.0'
);
}
/* wp-content/mu-plugins/client-admin/admin.css */
/* Hide a plugin's upsell notice that has no setting to disable it. */
.example-plugin-upsell-notice {
display: none;
}
/* Make the help widget stand out on the dashboard. */
#wsm_client_help {
border-left: 4px solid #0a48ac;
}
The $hook_suffix argument tells you which admin screen is loading, for example index.php for the dashboard or post.php for the editor. Use it to load styles only where they are needed.
Keep CSS hiding for cosmetic issues only. If you find yourself hiding a settings form with CSS, the client probably has a capability they should not have.
Step 8: Simplify the Block Editor
The block editor has its own settings. Two common client-friendly changes are disabling the Code editor and restricting which blocks appear in the inserter:
// wp-content/mu-plugins/client-admin.php
add_filter( 'block_editor_settings_all', 'wsm_client_editor_settings', 10, 2 );
function wsm_client_editor_settings( $settings, $context ) {
if ( ! current_user_can( 'manage_options' ) ) {
$settings['codeEditingEnabled'] = false; // Hides "Code editor" in the Options menu.
}
return $settings;
}
add_filter( 'allowed_block_types_all', 'wsm_client_allowed_blocks', 10, 2 );
function wsm_client_allowed_blocks( $allowed, $editor_context ) {
if ( current_user_can( 'manage_options' ) ) {
return $allowed; // Admins keep every block.
}
if ( isset( $editor_context->post ) && 'post' === $editor_context->post->post_type ) {
return array(
'core/paragraph',
'core/heading',
'core/list',
'core/list-item',
'core/image',
'core/gallery',
'core/quote',
'core/buttons',
'core/button',
'core/embed',
);
}
return $allowed;
}
Restricting blocks keeps news posts consistent with the site's design. Be careful applying an allow-list to pages or templates, because existing content that uses other blocks will show as unsupported. Patterns and block locking, covered in what are block patterns and how to create your own in WordPress, are often a better way to guide page layouts.
Testing the Client Experience
Never test only as an administrator, because every customization above is skipped for admins.
- Create a test user with the Site Manager role and a throwaway email address.
- Log in with that user in a private browser window.
- Check the dashboard, every remaining menu, the toolbar, and the block editor.
- Try visiting a removed screen directly, such as
/wp-admin/options-general.php. With the role above, WordPress should respond with "Sorry, you are not allowed to access this page." If the page loads, a capability needs to be removed, not just a menu item. - Publish, edit, and delete a test post to confirm the normal workflow still works.
Common Problems and Fixes
- A menu item comes back after you remove it. The plugin that adds it runs later. Raise the
admin_menupriority, or hook intoadmin_initfor menus registered very late. remove_menu_pagedoes nothing for a plugin menu. The slug is wrong. Read it from the menu link URL; foradmin.php?page=some-slug, usesome-slug.- The client can still reach a hidden page by URL. That is expected; menu removal is cosmetic. Remove the capability from the role instead.
- Role changes do not appear. Roles are stored in the database. Bump
WSM_CLIENT_ROLE_VERSIONso the role is rebuilt, or remove and re-add it once. - The client cannot see the Site Editor or Menus. They need
edit_theme_options. If you removed it on purpose, add only the specific screens they need through a different approach, such as synced patterns. - Customizations disappeared after a theme change. The code lived in
functions.php. Move it to a must-use plugin. - The update banner still shows. The
remove_actionpriority must be exactly3, and the removal must run before notices print, whichadmin_headat priority 1 handles.
WordPress Admin Customization FAQ
Usually not. An administrator can install plugins, edit users, and change settings that can break the site. Create a custom role based on Editor with only the extra capabilities the client needs, and keep a separate administrator account for maintenance.
No. Removing a menu item only hides the link. If the user has the capability, they can still open the page by typing its URL. Use roles and capabilities to restrict access and treat menu removal as cleanup.
In a plugin, ideally a must-use plugin in the wp-content/mu-plugins folder. It loads automatically, cannot be deactivated from the Plugins screen, and survives theme changes, unlike code in functions.php.
Yes, plugins such as role editors and admin menu editors provide a visual interface for the same changes. They are convenient for one-off sites, but code in a must-use plugin is easier to reuse across client sites and keep under version control.
The hooks used here, including admin_menu, wp_dashboard_setup, admin_bar_menu, and the label and footer filters, are long-standing public APIs. CSS that targets admin class names is more fragile, so keep it minimal and recheck it after major updates.
Find the widget ID by inspecting the widget box in your browser developer tools, then call remove_meta_box with that ID, the dashboard screen, and the widget context on the wp_dashboard_setup hook with a late priority.
Conclusion
A client-friendly WordPress admin comes from two layers working together. Roles and capabilities decide what the client can do, and they are the only layer that provides real protection. Menu, dashboard, toolbar, and CSS customizations decide what the client sees, and they turn a crowded admin into a focused workspace with a clear help widget, familiar labels, and the client's own branding.
Put everything in a must-use plugin so it survives theme changes, check manage_options so administrators keep the full interface, disable file editing in wp-config.php, and always test while logged in as the client role. The result is fewer support emails, fewer accidental changes, and a site the client actually feels comfortable running.
Here are some useful references for going deeper on customizing the WordPress admin:
- WordPress Developer Resources: Roles and Capabilities — how roles, capabilities, and add_role work.
- WordPress Developer Resources: Dashboard Widgets API — adding and removing dashboard widgets.
- WordPress Developer Resources: remove_menu_page() — function reference with notes on menu slugs.
- WordPress Developer Resources: Must Use Plugins — how mu-plugins load and their limitations.
- WordPress Developer Resources: WP_Admin_Bar::add_node() — adding and changing toolbar items.


