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:

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 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 );

Set a Custom Icon
Now let’s change a default icon to a custom one. There two options how you can set an icon:
- Use any icon from Dashicons set.
- 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 }>

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>
.

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 );

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:

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):
- After wrapping
TextControl
inwithSelect()
we have the possibility to accessmetaValue
property on line 30, - After wrapping it in
withDispatch()
we receive the possibility to usesetMetaValue()
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 ) } }
/>
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
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
Does the code on this page require your plugin?
On this page โย no.
Great tutorial Misha!
Having spent a few hours getting this working, here is a little tip to stop the sidebar meta fields ( or meta blocks ) clashing with custom fields on the post.
As documented ( here and elsewhere ) registering the meta fields will also add them as custom fields on your posts.
When you update the sidebar metabox, save the post and reopen it, the custom field ( if they are activated / visible ) will also contain the value of the sidebar meta field. Then when you try and update the sidebar meta field, and save the post for a second time, the value will be over written by the previous value which is still held in the custom field.
To stop this happening, protect the sidebar ( or block ) meta fields by prefixing the names with an underscore and providing a suitable authorization function.
For example:-
Details here: https://developer.wordpress.org/block-editor/tutorials/metabox/meta-block-2-register-meta/
Using a protected meta field stops it being listed in the post’s custom fields and solves the problem. Don’t forget to update the meta field name in the JS code too!
Thanks! ๐
Can I add a Colorpicker in this way? So the user can choose a color for the post, which a group of elements will be affected by it.
Cheers