Extend the Gutenberg Sidebar with a Dropdown Using JavaScript

Some time ago I wanted to learn how to extend the Gutenberg sidebar with a dropdown using JavaScript. From previous experience I knew the extension would use either JavaScript or PHP hooks. WordPress is usually easy to work with and I optimistically assumed that it would only take a couple hours at the most to look at the documentation and add the simple extension I wanted. Unfortunately I was wrong. I was very wrong. The documentation existed, but the example code was in a baffling new format that I had never seen before mixing JavaScript and HTML together in a way that was obviously incorrect syntactically. Furthermore, there were no plain vanilla JavaScript examples, even though WordPress had to be implementing it in the background. The following code examples are so that you don’t have to endure the same frustrations I went through.

First, I discovered that the WordPress documentation is in a format called JSX. (JSX stands for JavaScript XML). It is a syntax extension to JavaScript by React JS that will compile the embedded HTML into the proper output. But if you’re like me then you don’t want to deal with it because adding an extra step to compile JS code only complicates what should be a quick coding project.

Finally, after months of trawling through other people’s examples when I had spare time I was finally able to put together something that worked. And now I want to show you how I did it.

The Goal

My objective for this project was to extend the Gutenberg “Shortcode” block by adding a “<select>” dropdown with predefined shortcode tags to side menu in the block inspector. I want this dropdown to contain a list of possible shortcodes I could use to easily populate the shortcode block text area. Having all of my shortcodes in a predefined list will avoid typos.

Before we Extend the Gutenberg Sidebar with a Dropdown
Location where we want to add the sidebar

Extend the Gutenberg Sidebar with a Dropdown

The complete JavaScript code for extending the side menu looks like this. We start by declaring “MyCustomControls” as the name of the callback function which extends the block and finish by using wp.hooks.addFilter to run it during the “editor.BlockEdit” hook. This will extend the Gutenberg sidebar with a dropdown using JavaScript. We will break this down step by step.

// Load our JS code whenever Gutenberg is loaded
add_action('enqueue_block_editor_assets', 'LoadGutenbergScript');
function LoadGutenbergScript() {
    // Get script
    $script = GutenbergToolbarScript();
    
    // Ensure scripts we depend on are loaded
    wp_register_script('shortbus_gutenberg_toolbar', '', ['wp-blocks', 'wp-element', 'wp-i18n', 'wp-editor', 'wp-hooks'], '', true);
    
    // Output the script
    wp_enqueue_script('shortbus_gutenberg_toolbar');
    wp_add_inline_script('shortbus_gutenberg_toolbar', $script);
}
function GutenbergToolbarScript() {
    $script = '
        // Name a function to augment block control component with extra enhancements
        var MyCustomControls = wp.compose.createHigherOrderComponent(
            function(BlockEdit) {
                return function (props) {
                
                    // If this is NOT a shortcode block then return the default Primary Block Element
                        if (props.name === "core/shortcode") {
                        // Return our custom elements
                        return wp.element.createElement(
                            wp.element.Fragment,    // A container component which renders all children without a wrapping element
                            {},
                            // REQUIRED - Primary Block Element
                            wp.element.createElement(BlockEdit, props), // The default block component that is edited in Gutenburg
                            // Create element for the default Side Menu block inspector controls
                            wp.element.createElement(
                                wp.blockEditor.InspectorControls,    // Sidebar controls for the block
                                {},
                                // Create a panel element to hold our dropdown
                                wp.element.createElement(
                                    wp.components.PanelBody, 
                                    {}, 
                                    // Sidebar Dropdown
                                    wp.element.createElement(
                                    wp.components.SelectControl,
                                    {
                                        value: "",
                                        label: "Available Shortcodes",
                                        options: [
                                            {
                                                label: "- Insert Shortcode -",
                                                value: "",
                                            },
                                            {
                                                label: "--------",
                                                value: "",
                                                disabled: true,
                                            },
                                            {
                                                label: "Option #1",
                                                value: "[shortcode-example-1]",
                                            },
                                            {
                                                label: "Option #2",
                                                value: "[shortcode-example-2]",
                                            },
                                        ],
                                        onChange: function(value, event) {
                                            // Do something with the value
                                        },
                                    },
                                    )
                                    // END Sidebar Dropdown
                                )
                            )
                        );
                    }
              
                    return wp.element.createElement(BlockEdit, props);
                };
            },
            "MyCustomControls"    // The modifier name (similar to a plugin text domain namespace)
        );
        wp.hooks.addFilter(
            "editor.BlockEdit",    // The hook name that allows us to modify the focused block
            "MY_CUSTOM_CONTROL_NAMESPACE",    // Our custom namespace (similar to a plugin text domain)
            MyCustomControls    // Callback function to modify the block
        );
    ';
    return ($script);
}

After adding this code our dropdown will appear as desired.

After we Extend the Gutenberg Sidebar with a Dropdown
Dropdown in Sidebar after adding code to functions.php

Define the Callback Function

For now we will skip reviewing the PHP section of the code and will focus exclusively on the JavaScript found in the GutenbergToolbarScript() function.

The first thing we do is define the callback function. For example, we named it “MyCustomControlCallback” but you can give it whatever name you want. This callback function “wp.compose.createHigherOrderComponent” is used by WordPress to take a Gutenberg Block component and enhance it. The function takes 2 arguments: a mapComponent and a modifierName.

The modifierName is a seed name used to generate a display name for the enhanced block component.

The mapComponent is an example of JavaScript currying which is beyond the scope of this article. The oversimplified definition of currying is that it converts a function that takes multiple arguments into a sequence of nested functions, each taking a single argument. The first function takes the “BlockEdit” parameter (which is the default block component) and the second function takes the “properties” parameter which contains information about the block component. By default we will use the WordPress “wp.element.createElement” function to return the default (and unmodified) block component. All customization must be done before returning the default block component.

// Name a function to augment a block control component with extra enhancements
var MyCustomControlCallback = wp.compose.createHigherOrderComponent(
    function(BlockEdit) {
        return function (properties) {
            // ...
            
            // Return the unedited block component by default
            return wp.element.createElement(BlockEdit, properties);
        };
    },
    "MyCustomControls"    // The modifier name is a seed name used to generate a display name
);

Limit Customization to Specific Blocks

The next step in our code is to limit our block modifications to only the shortcode block. The hook that we use for modifying Gutenberg blocks is called for each and every block on the page and unless we impose a limitation we will be adding a dropdown to all Gutenberg blocks. That behavior might be useful in some circumstances, but not in this case. We use an conditional statement to only modify the block if its “name” property is equal to “core/shortcode”. Here’s a full list of Gutenberg’s core blocks.

// ...
// If this is a shortcode block then modify it
if (properties.name === "core/shortcode") {
    // ...
}
// ...

Prepare to Return the Custom Block Element

Inside the “if” condition we use “wp.element.createElement” to return the block with our enhancements added to it. This function accepts 3 arguments: type, props, and children. The type refers to the HTML tag or a specific created element. The props argument is for the properties of the type. And the children argument is an optional array of child elements.

We create the first element using a “wp.element.Fragment” element which is a component that acts as a container but renders its child elements without a wrapper. We pass an empty list of properties to this element.

The first child element of this fragment is the default Block element. This is block editor (paragraph, preformatted, shortcode, etc.) that you edit. We are able to pass the “BlockEdit” and “properties” parameters for it.

// ...
// Return our custom elements
return wp.element.createElement(
    wp.element.Fragment,    // A container component which renders all children without a wrapping element
    {},
    
    // REQUIRED - Primary Block Element
    wp.element.createElement(BlockEdit, properties), // The default block component that is edited in Gutenburg
    // ...
);
// ...
Default Block Edit Element
Default Block Edit Element

Include the Gutenberg Sidebar Inspector Controls

The second child of the fragment element (defined above) is for the sidebar inspector controls. Just as we defined the default block above, this is the default sidebar. We define it as a “wp.blockEditor.InspectorControls” element. This particular element has an empty object for it properties list. However, because we wanted to add our dropdown to the sidebar we will now start adding child elements.

// ...
// Create element for the default Side Menu block inspector controls
wp.element.createElement(
    wp.blockEditor.InspectorControls,    // Sidebar controls for the block
    {},
    
    // ...
)
// ...
Sidebar Inspector Controls
Sidebar Inspector Controls

Define the Container Panel

We are going to nest a couple of elements inside the sidebar control. Next, we will add a panel that will act as a container (in case we wanted to add multiple inputs or dropdowns) and inside of that we will add our dropdown. We define the panel as a type of “wp.components.PanelBody” with an empty set of properties.

// ...
// Create a panel element to hold our dropdown
wp.element.createElement(
    wp.components.PanelBody, 
    {}, 
    
    // ...
)
// ...
Panel that contains our custom elements
Panel that contains our custom elements

Define the Dropdown

With all of this preparation we are finally able to define our dropdown as a child of the panel element! We create the element as a type of “wp.components.SelectControl“. This tells WordPress that it will be a <select> HTML element. Next, we define its list of properties.

  • value – We give this an empty value, but could have pre-selected an option here.
  • label – The label to show above the dropdown.
  • options – The list of <option> elements that go inside the select. Each option can have its own set of attributes.
    • label – The text displayed in the option.
    • value – The actual value of the option
    • disabled – Boolean that determines if the option can be selected or not.
  • onChange – This event is triggered when a different option is selected. It has 2 parameters.
    • The value of the <select> dropdown.
    • The event that triggered the change.

Here is WordPress’s reference for all available Gutenberg components.

// ...
// Sidebar Dropdown
wp.element.createElement(
    wp.components.SelectControl,
    {
        value: "",
        label: "Available Shortcodes",
        options: [
            {
                label: "- Insert Shortcode -",
                value: "",
            },
            {
                label: "--------",
                value: "",
                disabled: true,
            },
            {
                label: "Option #1",
                value: "[shortcode-example-1]",
            },
            {
                label: "Option #2",
                value: "[shortcode-example-2]",
            },
        ],
        onChange: function(value, event) {
            // Do something with the value
            console.log(value);
        },
    },
)
// END Sidebar Dropdown
// ...

Add the JavaScript Hook

The final step to get our code to work is to attach our callback above to the correct hook. We do that using “wp.hooks.addFilter” and attaching it to the “editor.BlockEdit” hook. This must come after the callback function is defined or else you will receive a JavaScript error! The “wp.hooks.addFilter” function accepts up to 4 arguments: hookName, namespace, callback, and priority. The name of the hook we want is “editor.BlockEdit”. The namespace (used similarly to plugin text domains) can be anything you want, but in this case we used “MY_CUSTOM_CONTROL_NAMESPACE”. The callback was defined earlier as our “MyCustomControlCallback” function. And we simply did not use the optional priority argument.

wp.hooks.addFilter(
    "editor.BlockEdit",    // The hook name that allows us to modify the focused block
    "MY_CUSTOM_CONTROL_NAMESPACE",    // Our custom namespace (similar to a plugin text domain)
    MyCustomControlCallback    // Callback function to modify the block
);

Conclusion

It took a significant amount of code to extend a Gutenberg block with a single dropdown. But now that we know how to extend the Gutenberg sidebar with a dropdown we can modify our code to work with textboxes, checkboxes, and any other element we want.

Need more help? Check out our knowledge base for more help articles or contact us for help with your website.

Sign up to get our latest articles

Don’t worry. We won’t sell your email. We are also really busy managing our clients, so we won’t be filling your inbox with articles every day. We only write them when we have a compelling reason to do so, and some spare time too!

preloader