Creating Custom Sidebars with PluginSidebar

Creating Gutenberg sidebars and panels with fields inside of them is a brand new way of working with WordPress metadata. I am sure that the default way of creating meta boxes will stay with us for a long time as a backward compatibility, but believe me that working with the content using blocks and sidebars is a much more pleasant way than trying to configure everything in old-fashioned meta boxes.

So, what we are going to do in this tutorial?

Usually I like to show practical examples in my tutorials. And here is the one – I don’t use any SEO plugins on my projects, because I think they are too heavy and bloated with code. But if you’re familiar with SEO, you know that it is important to add unique titles and meta descriptions for your website pages. And probably you would like to hide some of them from indexing with robots meta tag.

create custom Gutenberg plugin sidebars
Our Gutenberg sidebar has 3 fields here – one text field, one textarea field and one toggle.

Please also stay away from tutorials where it is recommended to work directly with WordPress REST API and use wp.apiRequest etc. That’s not a correct way of creating Gutenberg sidebars.

Below is a step by step guide and a lot of JSX code. If you’re not sure what to do with it, maybe it is worth considering a way of creating sidebars with PHP?

1. Register Meta in REST API

I am going to use these 3 meta keys:

  • misha_plugin_seo_title – for SEO title text field,
  • misha_plugin_seo_description – for SEO description textarea field,
  • misha_plugin_seo_robots – for robots meta tag checkbox (or toggle in our case).

Nothing super especial right now, you just have to register all of the keys in REST API using register_meta() function.

// you have to use it within the 'init' hook
add_action( 'init', function(){

	register_meta( 
		'post', // object type, can be 'post', 'comment', 'term', 'user'
		'misha_plugin_seo_title', // meta key
		array(
			'object_subtype' => 'page', // you can specify a post type here
 			'type'           => 'string', // 'string', 'boolean', 'integer', 'number', 'array', and 'object'
 			'single'         => true, // one value per object or an array of values
 			'show_in_rest'   => true, // accessible in REST
 		)
	);

	register_meta( 
		'post', 
		'misha_plugin_seo_description', 
		array(
 			'type'		=> 'string',
 			'single'	=> true,
 			'show_in_rest'	=> true,
 		)
	);

	register_meta( 
		'post', 
		'misha_plugin_seo_robots', 
		array(
 			'type'		=> 'boolean', // because it is a checkbox
 			'single'	=> true,
 			'show_in_rest'	=> true,
 		)
	);

});

Why I used boolean type for robots meta key? Because I do not use anything except “noindex,follow”, so, the checkbox above will add only this value once checked.

One more thing – when registering a post type your supports parameter should contain custom-fields value, example:

register_post_type(
    'rudr_post_type_slug',
    array(
        ...
       'supports' => array( 'title', 'editor', 'custom-fields' ),
        ...
    )
);

2. Enqueue Scripts

I want to highlight this step too, because I think you have to pay attention to dependencies:

add_action( 'enqueue_block_editor_assets', function(){

	wp_enqueue_script(
		'misha-sidebar',
		get_stylesheet_directory_uri() . '/sidebar.js',
		array( 'wp-edit-post', 'wp-element', 'wp-components', 'wp-plugins', 'wp-data' ),
		filemtime( get_stylesheet_directory() . '/sidebar.js' )
	);

});

At you can see, comparing to registering a Gutenberg block, here is a lot of dependencies. The above code can be placed in your theme / child theme functions.php or in a custom plugin. You can see filemtime() function on line 7 – we need it to provide the new version of the file to wp_enqueue_script() function each time it was changed.

And I already know that I have to mention that too – at this moment sidebar.js is just an empty file 😁

In case you want to allow / disallow your sidebar depending on a custom post type, you can use get_current_screen()-based condition at the beginning of the action hook.

add_action( 'enqueue_block_editor_assets', function(){

	$screen = get_current_screen();
	if( 'page' === $screen->post_type ) {
		return; // disabled for Pages
	}

	wp_enqueue_script(

		...

3. registerPlugin()

The first time this tutorial was published in May 2019, the whole code was in raw JavaScript ES6, but now I work only with JSX and understand that there is no reason to support that 🙃 JSX is much simpler to use and read.

Let’s begin with something simple:

( function( wp ) {

	const { registerPlugin } = wp.plugins;
	const { PluginSidebar } = wp.editPost;

	registerPlugin( 'misha-seo', {
		render: function(){
			return (
				<PluginSidebar name="misha-seo" title="SEO">
					hello world
				</PluginSidebar>
			)
		}
	} );

} )( window.wp );
Simple sidebar for Gutenberg with default settings
It is the bare minimum of a Gutenberg sidebar. Just a custom text, default icon, no fields.

Set a Custom Icon

Now let’s change a default icon to a custom one. There are two options how you can set an icon:

  1. Use any icon from Dashicons set.
  2. Provide icon SVG code.
( function( wp ) {

	const { registerPlugin } = wp.plugins;
	const { PluginSidebar } = wp.editPost;
	
	// Custom SVG icon
	// const icon = <svg width="20" height="20"> ... </svg>;
	
	// Icon from Dashicons
	const icon = 'chart-area'; 

	registerPlugin( 'misha-seo', {
		render: function(){
			return (
				<PluginSidebar name="misha-seo" title="SEO" icon={ icon }>
how to set a custom icon for Gutenberg plugin sidebar
I used this icon from Dashicons set.

PluginSidebarMoreMenuItem

This component allows you to change how your sidebar plugin will look in “more” menu. By default it has the same title and icon as <PluginSidebar>.

PluginSidebarMoreMenuItem component
If we click on “Expand” button in the top right corner, we can see that our plugin displays under “Plugins” section with the same title and icon.

Let’s change it!

( function( wp ) {

	const { registerPlugin } = wp.plugins;
	const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost;
	const { Fragment } = wp.element;

	const icon = 'chart-area';

	registerPlugin( 'misha-seo', {
		render: function(){
			return (
				<Fragment>
					<PluginSidebarMoreMenuItem target="misha-seo" icon="palmtree">
						My super plugin
					</PluginSidebarMoreMenuItem>
					<PluginSidebar name="misha-seo" title="SEO" icon={ icon }>
						hello world
					</PluginSidebar>
				</Fragment>
			)
		}
	} );

} )( window.wp );
Custom title and icon for PluginSidebarMoreMenuItem

But I think it is better to use the same title and icon… well, at least the same icon.

PanelBody

Sometimes you probable would like to separate fields in your Gutenberg Sidebars by groups.

Like this:

how to create panels in Gutenberg sidebars

It is possible to implement with <PanelBody> component, the complete code is below:

( function( wp ) {

	const { registerPlugin } = wp.plugins;
	const { PluginSidebar } = wp.editPost;
	const { PanelBody } = wp.components;

	registerPlugin( 'misha-seo', {
		render: function(){
			return (
				<PluginSidebar name="misha-seo" title="SEO" icon="chart-area">
					<PanelBody title="Group 1" initialOpen={ true }>
						hello world
					</PanelBody>
					<PanelBody title="Group 2" initialOpen={ false }>
						some fields will be here
					</PanelBody>
				</PluginSidebar>
			)
		}
	} );


} )( window.wp );

I also have a tutorial where I describe how to hide some panels for specific post types.

4. Fields

Include necessary components

( function( wp ) {

	const { registerPlugin } = wp.plugins;
	const { PluginSidebar } = wp.editPost;
	
	const { 
		PanelBody, 
		TextControl, // text field
		TextareaControl, // textarea field
		ToggleControl // toggle field
	} = wp.components;

	const { withSelect, withDispatch } = wp.data;
	const { compose } = wp.compose;

Create a HOC (higher order component)

The long story short – using regular Gutenberg control components like TextControl or ToggleControl etc is not enough, because they don’t allow us go get or set values in post meta.

That’s why we compose them into a HOC (Higher Order Component):

  • After wrapping TextControl in withSelect() we have the possibility to access metaValue property on line 30,
  • After wrapping it in withDispatch() we receive the possibility to use setMetaValue() on line 32 which allows to update the meta values correctly.
const MishaTextControl = compose(
		withDispatch( function( dispatch, props ) {
			return {
				setMetaValue: function( value ) {
					dispatch( 'core/editor' ).editPost( { meta: { [props.metaKey]: value } } );
				}
			}
		} ),
		withSelect( function( select, props ) {
			return {
				metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ],
			}
		} )
	)( function( props ) {

		return (
			<TextControl
				type="text"
				label={ props.label }
				value={ props.metaValue }
				onChange={ ( content ) => { props.setMetaValue( content ) } }
			/>
		);
	} );

If we are going to create all 3 type of fields – seo title, description and toggle, we have to create higher order components for every inspector control – MishaTextControl, MishaTextareaControl and MishaCheckboxControl, but the components code is very similar, so for some situations it would be better to create just one component, let’s say MishaControl and pass control type in it.

Maybe it is a little bit different for <ToggleControl>, but that’s all.

<ToggleControl
	label={ props.label }
	checked={ props.metaValue }
	onChange={ ( content ) => { props.setMetaValue( content ) } }
/>

Using useSelect/useDispatch instead of withSelect/withDispatch

In the official WordPress documentation is is said that there are no plans to make withSelect and withDispatch deprecated in favor of new useSelect and useDispatch hooks.

But anyways, it is up to decide which way to use, I decided to explain both of them.

First things first you need to use const { useSelect, useDispatch } = wp.data instead of const { withSelect, withDispatch } = wp.data and you don’t need to import compose anymore.

Second, let’s just update the code of MishaTextControl component.

const MishaTextControl = ( props ) => {

	const { metaValue } = useSelect( ( select ) => {
		return {
			metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ],
		}
	}, [ props.metaKey ] );

	const { editPost } = useDispatch( 'core/editor' );

	return (
		<TextControl
			type="text"
			label={ props.label }
			value={ metaValue }
			onChange={ ( value ) => editPost( { meta: { [props.metaKey]: value } } ) }
		/>
	)

}

Add fields to sidebar

The last step is to add our component fields to <PluginSidebar> and wrap them into <PanelBody>.

<PluginSidebar name="misha-seo" title="SEO" icon="chart-area">
	<PanelBody>
		<MishaTextControl label="Title" metaKey="misha_plugin_seo_title" />
		<MishaTextareaControl label="Description" metaKey="misha_plugin_seo_description" />
		<MishaToggleControl label="Hide from search engines" metaKey="misha_plugin_seo_robots" />
	</PanelBody>
</PluginSidebar>

Creating Gutenberg Sidebars with PHP

I completely understand that sometimes there is no time to configure Webpack, Babel and all that stuff for your WordPress project and maybe you’re not so good at React.js.

So I create the plugin that allows to create sidebars and fields for them with pure PHP. For now let’s create the same SEO fields we created above in this tutorial.

  1. Install and activate my Simple Fields plugin on your WordPress website.
  2. Copy and paste the following code into your functions.php file or to a custom plugin:
add_filter( 'simple_register_sidebars', 'rudr_seo_fields' );

function rudr_seo_fields( $sidebars ) {

	$sidebars[] = array(
		'id' => 'misha-seo',
		'name' => 'SEO',
		'icon' => 'chart-area',
		'fields' => array(
			array(
				'id' => 'seo_title',
				'label' => 'Title',
				'type' => 'text'
			),
			array(
				'id' => 'seo_description',
				'label' => 'Description',
				'type' => 'textarea',
				'rows' => 3
			),
			array(
				'id' => 'seo_robots',
				'type' => 'checkbox',
				'label' => 'Hide from search engines',
			),
		),
	);

	return $sidebars;

}

That’s really all. Do you believe me? Here is our result:

Create Gutenberg sidebar fields with PHP
Yes, yes, I know, here we have a checkbox, not a toggle field. But what can I say? My goal was to make my Simple Fields plugin as simple as possible, and there is no actual difference between using checkboxes and toggle fields.
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X