Gutenberg Sidebars Tutorial

Welcome to the correct tutorial about creating Gutenberg sidebar panel settings. I’m saying correct, because when I learn this I found out that some tutorials suggest you to work directly with WordPress REST API and use wp.apiRequest etc. Please skip those tutorials, because those implementations will cause bugs.

If you need some custom Gutenberg development help, you can always contact me.

Letโ€™s begin! ๐Ÿš€

First of all, what we are going to create in this tutorial?

Usually I like to show practical examples in my tutorials. And here is 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.

And here is our example:

Creating Gutenberg Sidebars for custom post settings
Our Gutenberg sidebar has 3 fields here โ€“ one text field, one textarea field and one toggle.

1. Register Meta in REST API

I am going to use these 3 meta keys:

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 are 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 / disable 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 once I worked with JSX, I understand that there is no reason t 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 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>.

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

Step 4.1 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;

Step 4.2 Create a 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):

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 ) } }
/>

Step 4.3 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>

If you need some custom Gutenberg development help, you can always contact me.

Misha Rudrastyh

Misha Rudrastyh

I develop websites since 2008, so it is total of 13 years of experience, oh my gosh. Most of all I love love love to create websites with WordPress and Gutenberg, some ideas and thoughts I share throughout my blog.

Need some developer help? Contact me