Bye-bye Meta Boxes, Hello Gutenberg Sidebars
By the way, the tutorial about WordPress Meta Boxes is my first tutorial on this blog, but now time of Meta Boxes is over…
Below is the correct tutorial about creating Gutenberg sidebar panel settings. I say correct, because when I learned this I found some tutorials which suggest you to work directly with WordPress REST API and use wp.apiRequest
etc. Please skip such tutorials, because those implementations will cause bugs.
Let’s begin! π
I personally don’t work with Yoast plugin, not because I do not like it, I just think that it is too heavy, so I store my posts SEO settings in post meta. And here is what we will create in this tutorial:

The cool thing here is that you can create as much these sidebar “tabs” as you want and each of them can contain a lot of fields. You can deactivate a certain sidebar tab if you click on the star icon.
One more thing βΒ if you do not want deal with all of the code below, I recommend you an alternate way to create Gutenberg sidebars β just with small PHP code snippets for the functions.php
file.
Step 1. Register Meta Keys in REST API
I am going to use these 3 meta keys β misha_plugin_seo_title
, misha_plugin_seo_description
and misha_plugin_seo_robots
.
Nothing super especial right now, you just have to register all of the keys in REST API. Code for your functions.php
/ custom plugin:
// you have to use it within the "init" hook add_action( 'init', function(){ register_meta( 'post', 'misha_plugin_seo_title', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, ) ); 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, ) ); });
Of course, you can check the documentation of the register_meta()
function, but I will describe the parameters we used here shortly.
post
β object type we register meta for. Object types arepost
,term
,comment
oruser
, so for Gutenberg sidebars we are going to usepost
only.type
β the type of data, can bestring
,boolean
,integer
ornumber
,single
equal totrue
means, that our meta has only one value.
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.
Step 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-i18n', 'wp-blocks', 'wp-edit-post', 'wp-element', 'wp-editor', 'wp-components', 'wp-data', 'wp-plugins', 'wp-edit-post' ), filemtime( dirname( __FILE__ ) . '/sidebar.js' ) ); });
At you can see, comparing to registering a regular 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, but please be careful with the path on line 7.
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( $screen->post_type === 'page' ) return; // disabled for Pages wp_enqueue_script( ...
Step 3. Components
In this step let’s decide which React components we will need.
( function( plugins, editPost, element, components, data, compose ) { const el = element.createElement; const { Fragment } = element; const { registerPlugin } = plugins; const { PluginSidebar, PluginSidebarMoreMenuItem } = editPost; const { PanelBody, TextControl, TextareaControl, CheckboxControl } = components; const { withSelect, withDispatch } = data; const MetaTextControl = ... const MetaTextareaControl = ... const MetaCheckboxControl = ... registerPlugin( ... ); } )( window.wp.plugins, window.wp.editPost, window.wp.element, window.wp.components, window.wp.data, window.wp.compose );
Here is just a skeleton, how your sidebar.js
file will look like. You can copy the above code to the file right away.
Probably you see here some familiar components, like PanelBody
, TextControl
etc on line 7, which were explained in details in my previous tutorial. All the new components I will describe below.
Step 4. registerPlugin
Ok, here is a lot of code but everything is explained after it.
registerPlugin( 'misha-seo', { render: function() { return el( Fragment, {}, el( PluginSidebarMoreMenuItem, { target: 'misha-seo', icon: mishaIcon, }, 'SEO' ), el( PluginSidebar, { name: 'misha-seo', icon: mishaIcon, title: 'SEO', }, el( PanelBody, {}, // Field 1 el( MetaTextControl, { metaKey: 'misha_plugin_seo_title', title : 'Title', } ), // Field 2 el( MetaTextareaControl, { metaKey: 'misha_plugin_seo_description', title : 'Description', } ), // Field 3 el( MetaCheckboxControl, { metaKey: 'misha_plugin_seo_robots', title : 'Hide from search engines', } ), ) ) ); } } );
mishaIcon
is a variable you have to set before which either points to an icon from Dashicons set (example:admin-post
) or contains a SVG icon. Read this tutorial to learn more.- Do not forget about
PluginSidebarMoreMenuItem
β it just adds your sidebar panel to “Plugins” section here:

MetaTextControl
,MetaTextareaControl
andMetaCheckboxControl
are just my custom components which extend as you could guessTextControl
,TextareaControl
andCheckBox
control. We have to do it, because it is not enough just to display certain fields in sidebar panels, we have to get their values from post meta and update the values as well.
Step 5. withDispatch, withSelect
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.
var MetaTextControl = compose.compose( withDispatch( function( dispatch, props ) { return { setMetaValue: function( metaValue ) { dispatch( 'core/editor' ).editPost( { meta: { [ props.metaKey ]: metaValue } } ); } } } ), withSelect( function( select, props ) { return { metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ], } } ) )( function( props ) { return el( TextControl, { label: props.title, value: props.metaValue, onChange: function( content ) { props.setMetaValue( content ); }, }); } );
According to the step 4 I created higher order components for each inspector control β MetaTextControl
, MetaTextareaControl
and MetaCheckboxControl
, but the components code is very similar, so for some situations it would be better to create just one component, let’s say MetaControl
and pass control type in it.
And a small bonus for you β just in case your would like to hide certain controls in your sidebar depending on a custom post type:
withSelect( function( select, props ) { return { metaValue: select( 'core/editor' ).getEditedPostAttribute( 'meta' )[ props.metaKey ], postType: select("core/editor").getCurrentPostType(), } } ) )( function( props ) { if ( props.postType == 'page' ) { return null; } return el( TextControl, { ...
Comments — 5
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
Comments are closed.