< Retour au blog

Adding options and controls to an existing Gutenberg block

Publié le

dans la catégorie

,

Lire la version en français

This article explains how to add options and controls to existing blocks in the WordPress editor, Gutenberg, and save them in the attributes.

The procedure is different from adding controls when creating custom blocks, as we will only use filters to hook to existing blocks. The filters available in the editor work like PHP filters (add_filter, apply_filters).

It’s best if you have a basic knowledge of React and/or JavaScript to understand this tutorial. I won’t go into details about the concepts related to components, etc.

I invite you to consult the documentation on filters before reading this article.

All the functional code is available as a plugin.

Steps to add a control

Adding a custom control consists of four steps:

  1. Define a custom attribute to store the value of the control
  2. Add the control to the desired location
  3. Manipulate the rendering of the block
  4. Save the block

For this first example, we will add a button to the Toolbar of the paragraph block. The whole code is visible on the plugin repository.

Define a custom attribute

If you want to add a control to a block, it is probably because you want to save its value along with the other values of the block.

Just like custom fields in a post, the attributes of a block must be declared first. When creating a custom block, this is done at block declaration time.
On the other hand, if we wish to add an attribute to an existing block (native or not), we must use a filter: blocks.registerBlockType.

const enableToolbarButtonOnBlocks = [ 'core/paragraph' ]; const setToolbarButtonAttribute = ( settings, name ) => { // Do nothing if it's another block than our defined ones. if ( ! enableToolbarButtonOnBlocks.includes( name ) ) { return settings; } return Object.assign( {}, settings, { attributes: Object.assign( {}, settings.attributes, { paragraphAttribute: { type: 'string' } } ), } ); }; wp.hooks.addFilter( 'blocks.registerBlockType', 'custom-attributes/set-toolbar-button-attribute', setToolbarButtonAttribute );
Code language: JavaScript (javascript)

In the code above, in setToolbarButtonAttribute we add the paragraphAttribute, specifying that it is a string. You will customize paragraphAttribute according to your control.

Next, we add this to the paragraph block by hooking into wp.hooks.addFilter, blocks.registerBlockType, which is a filter performed on each block declaration.

A word about the condition at the beginning of the code:

if ( ! enableToolbarButtonOnBlocks.includes( name ) ) { return settings; }
Code language: JavaScript (javascript)

It simply checks that the current block is part of an authorized list that is defined in enableToolbarButtonOnBlocks.

Note that if your custom control is to be saved on an existing attribute (for example the align attribute), you can skip this first step.

Add the control to the block

Once the attribute is defined, we add the control to the block so that the attribute can be modified in the editor. In this first example, we add a button to the Toolbar:

const withToolbarButton = createHigherOrderComponent( ( BlockEdit ) => { return ( props ) => { // If current block is not allowed if ( ! enableToolbarButtonOnBlocks.includes( props.name ) ) { return ( <BlockEdit { ...props } /> ); } const { attributes, setAttributes } = props; const { paragraphAttribute } = attributes; return ( <Fragment> <BlockControls group="block"> <ToolbarGroup> <ToolbarButton icon="format-status" label={ __( 'Custom Button', 'core-block-custom-attributes' ) } isActive={ paragraphAttribute === 'custom' } onClick={ () => { if ( paragraphAttribute === 'custom' ) { setAttributes( { paragraphAttribute: false } ) } else { setAttributes( { paragraphAttribute: 'custom' } ) } } } /> </ToolbarGroup> </BlockControls> <BlockEdit { ...props } /> </Fragment> ); }; }, 'withToolbarButton' ); wp.hooks.addFilter( 'editor.BlockEdit', 'custom-attributes/with-toolbar-button', withToolbarButton );
Code language: JavaScript (javascript)

First, we use createHigherOrderComponent, which allows to modify an existing component and to access its properties, in order to modify the Toolbar.

Then we use BlockControls to position ourselves in the Toolbar, then ToolbarGroup, and finally we create our button with the ToolbarButton component. This is where the addition of the custom value of the attribute, and its removal are managed, in the onClick method.

This time we use the editor.BlockEdit filter, which is performed in the Edit functions of the blocks.

About the group="block" attribute on the BlockControls: it allows to be positioned at the same level as the controls added to the block (in its declaration).

If you remove it, your control will be added in a container following these controls.

Manipulating the render of the block in the editor

At this point, our control is displayed and the attribute is added or removed when the button is clicked.

This is not visible, as the attribute itself does not change anything. If you want to use the attribute via HTML, you have to add a CSS class to the block according to the attribute.

To do this, we will use the editor.BlockListBlock filter which allows us to modify the properties (props) of the block wrapper, and therefore to add a CSS class or an HTML attribute, for example.

const withToolbarButtonProp = createHigherOrderComponent( ( BlockListBlock ) => { return ( props ) => { // If current block is not allowed if ( ! enableToolbarButtonOnBlocks.includes( props.name ) ) { return ( <BlockListBlock { ...props } /> ); } const { attributes } = props; const { paragraphAttribute } = attributes; if ( paragraphAttribute && 'custom' === paragraphAttribute ) { return <BlockListBlock { ...props } className={ 'has-custom-attribute' } /> } else { return <BlockListBlock { ...props } /> } }; }, 'withToolbarButtonProp' ); wp.hooks.addFilter( 'editor.BlockListBlock', 'custom-attributes/with-toolbar-button-prop', withToolbarButtonProp );
Code language: JavaScript (javascript)

In this code, we simply check that our paragraphAttribute contains the value “custom”, and if so, we add a has-custom-attribute CSS class. If not, we return the original properties of the block.

Save the block

This is the last step, we now need to save the block.

This is quite similar to the previous step: if our attribute is present and contains the value “custom”, we add the has-custom-attribute class to the wrapper.

This time we are on the blocks.getSaveContent.extraProps filter, which allows us to save custom properties (here, a CSS class) for the block.

const saveToolbarButtonAttribute = ( extraProps, blockType, attributes ) => { // Do nothing if it's another block than our defined ones. if ( enableToolbarButtonOnBlocks.includes( blockType.name ) ) { const { paragraphAttribute } = attributes; if ( paragraphAttribute && 'custom' === paragraphAttribute ) { extraProps.className = classnames( extraProps.className, 'has-custom-attribute' ) } } return extraProps; }; wp.hooks.addFilter( 'blocks.getSaveContent.extraProps', 'custom-attributes/save-toolbar-button-attribute', saveToolbarButtonAttribute );
Code language: JavaScript (javascript)

To summarize: we have added a button to the Toolbar of the Paragraph block. This button allows to add or remove the “custom” value of the attribute named paragraphAttribute, and when it has this value, the Paragraph will have the CSS class “has-custom-attribute”.

Second example

In this second example, we add a selector in the Sidebar of the Image block. The goal is to have a list of options available, and that the value of the option is used to build a dynamic CSS class.

Again the complete code is visible on the repository.

The declaration of the attribute does not change compared to the first example, except for the name of the methods and the key of the attribute.

However, the addition of the control varies, since this time we add our control in the Sidebar and not in the Toolbar:

return ( <Fragment> <BlockEdit { ...props } /> <InspectorControls> <PanelBody title={ __( 'Image Custom Attributes' ) } > <SelectControl label={ __( 'Custom Attribute' ) } value={ imageAttribute } options={ [ { label: __( 'None' ), value: '' }, { label: __( 'One' ), value: 'one' } ] } onChange={ ( value ) => { setAttributes( { imageAttribute: value, } ); } } /> </PanelBody> </InspectorControls> </Fragment> );
Code language: JavaScript (javascript)

For this we use the InspectorControls component, which allows us to position ourselves in the Sidebar, then we add a Panel component and finally our Select. It is in the latter that the value of the attribute is defined according to the chosen option.

The management of the editing of the block wrapper and its saving does not change much compared to the first example, we still assign a class to the block wrapper, and it consists of a dynamic part: the option chosen in the Select.

className={ 'has-option-' + imageAttribute }
Code language: JavaScript (javascript)

So when we choose the “One” option, the Image block will have the “has-option-one” class.

To go further

If you want to modify the plugin created for this tutorial, to develop your own controls for example, download the plugin folder, install the dependencies (npm install) and use the documentation to know the available commands.

Finally, don’t hesitate to leave your comment on this article! Thanks.

Translated with deepl.com

15 responses to “Adding options and controls to an existing Gutenberg block”

  1. It’s Really very Helpfull, Thanks for this amazing Explaination.

    I implemented it, but I just had one issue like whenever I select Multiple Same Blocks at a time using [CTRL + A] the Custom Toolbar Button Added is visible, but if I select Multiple Different Blocks then the Toolbar Button is not visible.

    Can you please suggest a solution for that?

  2. Brilliant, thank you very much! I needed the dropdown for the sidebar and your site was the first result on google, a very lucky find! I find asking clients to type in CSS classes manually doesn’t work in real life, so having a dropdown is a realistic approach to adding classes. I wish WP would make a Class dropdown a default, with a filter where classes can be set in functions.php but this is the next best thing.

      • Hi Marie, oh wow, thank you! I hadn’t noticed that before, yes, that’s a great solution!

        Can I ask – in your plugin you add a dropdown. What is the benefit of the dropdown compared to Bock Style?

        Or to ask differently: What can a sidebar dropdown do that’s not possible with Block Style?

        • Hi Edith,

          Block Styles is relevant if you just need to add a class. And it’s convenient because WordPress itself handle adding/removing classes. Plus, no need of JavaScript, just PHP.

          But, Block Style is not relevant if your selectable option is not related to block style, or if it’s contain a lot of options, or if you want to add something else than a class.

          Imagine an Icon Block, which let you to select the icon to display, and its font weight (regular, bold, thin).
          The icon font weight is a style, limited to 3 options, so it will be managed by the Block Style.
          But the icon select, which may contain dozens of options, will be more relevant in a separate dropdown.

          So your Icon Block HTML markup will look like this :

          <div class="wp-block-icon is-style-$weight" data-icon="icon-$icon-name">...</div>

          Where data-icon will be added by your custom code, and $icon-name take the value of the selected icon.
          And the classname is-style-$weight will be added by WordPress itself.

          I hope my explanation is clear 🙂

  3. @Marie.

    Just ignore my previous comment. I found the issue. I was blunded when did not realize that the structure of my repository is different.

    Your code is just amazing.

    Could I ask: instead of adding “has-custom-attribute” to the paragraph block className, how could I modify the content of the paragraph?

    Thanks a lot.

  4. I downloaded the plugin from the GitHub repo, but it doesn’t seem to be adding the custom className when the Select menu is changed and after the page is saved and reloaded — does this code need to be updated? Thanks in advance, this tutorial is very helpful!

  5. Hi,

    Great code, I found that when implementing the code the select box is duplicated many times on the sidebar, any idea why this could happen and how can this be avoided?

  6. Hi, I’ve been looking for something like this for a while. I have got it working on a Group block. It is used to select from a list of icon names. Ideally I would like the custom attribute dropdown to only show if a particular Group Style is selected.

    So for example I have a Group Style called ‘Icon Group’ and the dropdown only really applies when that is selected. I was looking to see if there is a conditional statement that would only show the Select box if the ‘Icon Group’ style is selected. It doesn’t look like that is possible though?

    Thanks,
    Ben

  7. Hello, this article explains the topic most clearly and accurately, the only thing missing is the PHP implementation for managing props on the frontend, but this is still the best article on the entire Internet, it’s a pity that the WordPress documentation cannot look like this. Thanks for your work 🙂

Leave a Reply to Alex Mustin Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.