When you build a WordPress plugin settings page with @wordpress/components and @wordpress/scripts, the buttons, toggles and panels often look unstyled. Outside the block editor, WordPress does not load the component stylesheet for you, even though your script depends on wp-components.
This snippet loads the core wp-components styles on your settings page, and only there, so the controls look the same as in the editor.
The snippet
<?php
/**
* Load the WordPress component (Gutenberg) styles on your own plugin settings page.
*/
// ===== Config =====
function dpc_component_styles_config() {
$config = array(
// Hook suffix of each settings page, as returned by add_menu_page() / add_submenu_page(),
// e.g. 'toplevel_page_my-plugin' or 'settings_page_my-plugin'.
'screens' => array( 'settings_page_my-plugin' ),
// Core style handles to load there. Add 'wp-block-editor' if the page renders block editor parts.
'styles' => array( 'wp-components' ),
);
// ===== End of config =====
return (array) apply_filters( 'dpc_component_styles_config', $config );
}
add_action( 'admin_enqueue_scripts', 'dpc_component_styles' );
function dpc_component_styles( $hook_suffix ) {
$config = dpc_component_styles_config();
if ( ! in_array( $hook_suffix, (array) ( $config['screens'] ?? array() ), true ) ) {
return;
}
foreach ( (array) ( $config['styles'] ?? array() ) as $handle ) {
if ( wp_style_is( $handle, 'registered' ) ) {
wp_enqueue_style( $handle );
}
}
}
Add it with Scripts Organizer
In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Admin only, tick PHP under Script location, paste the code into the PHP editor, edit the config and publish. Leave Action/Hook empty. Admin only fits: settings pages only exist in wp-admin, and the enqueue runs on admin_enqueue_scripts, the hook WordPress expects for admin styles.
The same code also works unchanged in a small plugin or an mu-plugin.
How it works
- WordPress registers the
wp-componentsstyle handle itself, pointing towp-includes/css/dist/components/style.css. The snippet only enqueues that handle, so the version and the RTL file come from core and stay current after updates. admin_enqueue_scriptspasses the hook suffix of the current screen. The styles are added only when it is in yourscreenslist, so other admin pages do not load an extra stylesheet.- Handles that are not registered are skipped, so a typo in
stylescannot cause a notice. - The earlier version of this post called
wp_enqueue_style()at the top level of the file. That runs before WordPress is ready for styles and logs “wp_enqueue_style was called incorrectly”. It also pointed a custom URL at thewp-editorhandle name.
Find your hook suffix
add_menu_page() and add_submenu_page() return the hook suffix, for example toplevel_page_my-plugin for a top-level page or settings_page_my-plugin for a page under Settings. Not sure? Temporarily add error_log( $hook_suffix ); inside dpc_component_styles() and open your page.
Customise it
- Several pages: add more hook suffixes to
screens. - Block editor parts on the page (for example
BlockEditorProvider): add'wp-block-editor'tostyles; for core block styles add'wp-block-library'. - In your own plugin you can skip this snippet and list
wp-componentsas a dependency of your stylesheet:wp_enqueue_style( 'my-plugin', $url, array( 'wp-components' ), $version );.
Gotchas
- The styles are written for the editor. A few admin styles such as
.wrap h1or form inputs may still win over component styles on a classic settings page; scope your own CSS to the root element of your React app. - Since WordPress 6.x the component styles use CSS custom properties for the admin colour scheme, so they follow the user’s chosen colours.
- For editor-only CSS, see CSS In Gutenberg editor.
In the Scripts Organizer library Import it with one click from Scripts Organizer.