How to Create Inspector Controls for a Custom Gutenberg Block
First of all we create some kind of useful options for our custom block like this:

Once we do it, I will show you how to add all kind of fields too like: textarea, checkbox, number, radio buttons and of course we will try to create multiple options panels.

1. Components We Need
I highly encourage you to read the first and the second tutorial in this series. Because some things may be difficult to understand in this case.
Here is what we need:
Fragment
andInspectorControls
as wrappers,Panel
,PanelBody
,PanelRow
to create options panels,TextControl
,ToggleControl
for our settings fields.
Let’s dive into it!
( function( blocks, editor, element, components ) {
const el = element.createElement;
const { registerBlockType } = blocks;
const { RichText, InspectorControls } = editor;
const { Fragment } = element;
const { TextControl, ToggleControl, Panel, PanelBody, PanelRow } = components;
attributes: {
// ...
}
registerBlockType( // ...
edit: function( props ) {
// ...
}
save: function( props ) {
// ...
}
)
} )(
window.wp.blocks,
window.wp.editor,
window.wp.element,
window.wp.components,
);
2. Deal with Attributes
Let’s add some attributes. At this moment we are going to add just two fields, so:
attributes: {
// ...
list_id: {
type: 'string',
// default: '12345' // you can set a default value
},
doubleoptin: {
type: 'boolean' // for ToggleControl we need boolean type
}
},
Nothing especial here, we added an attribute for each of our fields, and as you probably noticed, for ToggleControl
and CheckboxControl
we have to use boolean type.
3. Add Panels and Settings Fields
In this part of the tutorial you will understand why everyone uses babel js to transform React this way π
<Fragment>
<InspectorControls>
<PanelBody ...
Maybe one day I will learn npm and all that stuff… but it is not today π Because everything I want today is to make my website and my plugins Gutenberg-compatible. And I have a team to develop clients websites, so I do not have to know it myself.
And now let’s make some settings. Look at your edit()
function which is a part of registerBlockType()
as you probably remember.
edit: function( props ) {
return (
el( Fragment, {},
el( InspectorControls, {},
el( PanelBody, { title: 'Form Settings', initialOpen: true },
/* Text Field */
el( PanelRow, {},
el( TextControl,
{
label: 'List ID',
onChange: ( value ) => {
props.setAttributes( { list_id: value } );
},
value: props.attributes.list_id
}
)
),
/* Toggle Field */
el( PanelRow, {},
el( ToggleControl,
{
label: 'Double Opt In',
onChange: ( value ) => {
props.setAttributes( { doubleoptin: value } );
},
checked: props.attributes.doubleoptin,
}
)
)
),
),
/*
* Here will be your block markup
*/
),
)
},
4. Make Changes in the Block Markup Depending on its Settings
To be honest, I’m not sure that I created the markup below the perfect way π If you know how to do it better, please let me know in comments below.
Actually all I did below β added two hidden fields and printed the value of our settings there.
save: function( props ) {
return (
el( 'form', { className: 'misha-subscription-block-form-wrap' },
el( 'input', { 'type': 'email', 'placeholder' : 'Enter your email address' } ),
el( 'input', { 'type': 'hidden', 'name' : 'list_id', 'value' : props.attributes.list_id } ),
el( 'input', { 'type': 'hidden', 'name' : 'double_opt_in', 'value' : ( props.attributes.doubleoptin == true ? 'yes' : 'no' ) } ),
el( 'button', {}, 'Subscribe' )
)
);
},
But depending on your situation you may probably want to add some CSS classes to the form or maybe even display it only for registered users / certain user roles.
And one more thing β hidden fields don’t change form appearance, that’s why we added them only in save()
function, but you can also change block appearance in Gutenberg (edit()
function) depending on attributes values.
More Awesome Fields
If you remember the second screenshot from the beginning of this tutorial, I also decided to add the second panel with: TextareaConrol
, CheckboxControl
, SelectControl
, RadioControl
and RangeControl
.

Let’s begin with the appropriate components first:
const {
TextControl,
CheckboxControl,
RadioControl,
SelectControl,
TextareaControl,
ToggleControl,
RangeControl,
Panel,
PanelBody,
PanelRow
} = components;
When it comes to attributes, I would say, that nothing especial there, just duplicate the attributes we created before for TextControl
and ToggleControl
and change their names.
And here is the controls code:
el( InspectorControls, {},
// 1st Panel β Form Settings
el( PanelBody, { title: 'Form Settings', initialOpen: true },
// Text field
el( PanelRow, {},
el( TextControl,
{
label: 'List ID',
onChange: ( value ) => {
props.setAttributes( { list_id: value } );
},
// type: 'number', // in case it is a number field
value: props.attributes.list_id
}
)
),
// Toggle
el( PanelRow, {},
el( ToggleControl,
{
label: 'Double Opt In',
onChange: ( value ) => {
props.setAttributes( { doubleoptin: value } );
},
checked: props.attributes.doubleoptin,
}
)
)
),
// 2nd Panel β Awesome Fields (closed by default, see initialOpen parameter)
el( PanelBody, { title: 'Awesome fields', initialOpen: false },
// Textarea field
el( TextareaControl,
{
label: 'Textarea Control',
onChange: ( value ) => {
props.setAttributes( { textarea_attr: value } );
},
value: props.attributes.textarea_attr,
}
),
// Checkbox field
el( CheckboxControl,
{
label: 'Checkbox Control',
onChange: ( value ) => {
props.setAttributes( { chekbox_attr: value } );
},
checked: props.attributes.chekbox_attr,
}
),
// Select dropdown field
el( SelectControl,
{
label: 'Select Control',
options : [
{ label: 'Option 1', value: 'val_1' },
{ label: 'Option 2', value: 'val_2' },
],
onChange: ( value ) => {
props.setAttributes( { select_attr: value } );
},
value: props.attributes.select_attr
}
),
// Radio buttons
el( RadioControl,
{
label: 'Radio Control',
//help: 'Some kind of description',
options : [
{ label: 'Option 1', value: 'value_1' },
{ label: 'Option 2', value: 'value_2' },
],
onChange: ( value ) => {
props.setAttributes( { radio_attr: value } );
},
selected: props.attributes.radio_attr
}
),
// Range
el( RangeControl,
{
label: 'Range Control',
min: 2,
max: 10,
onChange: ( value ) => {
props.setAttributes( { range_attr: value } );
},
value: props.attributes.range_attr
}
),
)
),
Iβm pretty sure you will have question about this tutorial, so welcome to comments!
Read also:

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
Thank you very much, I read a lot of your tutorials to train me on gutenberg …
Always welcome!
If you have any suggestions for new posts on Gutenberg, just let me know πβ‘οΈ
Hi misha! Your explanations are always very clear!
I wanted to know if you have any idea how to create conditional rules to show or hide some component in the sidebar …
for example using SelectControl I would like if the “image background” option is selected all the options related to the image are shown; while if the “background color” option is selected all the options relating to the images disappear and only those of the background colors are shown
+1. Wondering about this too!
Awesome work man, I spent hours to find that how to add SidebarControls in Gutenberg, and you have written it in detail and save my days. Thanks,
You’re welcome! :)
Thanks for the help!
Like the post and help me to understand they way to modify Inspector component. One thing concern me though, why did you want to create ‘el’? The code just doesn’t look standard of JSX way…
Not a lot of quality tutorials for adding Gutenberg blocks, especially using es5 code. Thanks!