How to build a block editor sidebar using vanilla JavaScript

on in WordPress
Last modified on

This article will explain how I moved away from the True Metabox plugin and coded my own sidebar for a custom post type.

I need a custom post to have 2 specific meta fields. Now, I could add these as standard metaboxes, but I wanted to experiment with creating a sidebar and have the native feel of the block editor.

Note that the Block Editor Handbook explains in detail how to create a plugin sidebar, but there is no vanilla JavaScript code, and sometimes, you just don’t need the extra dependencies and hassle that React requires.

Block Editor Sidebar
Block Editor Sidebar

My new sidebar has a custom icon and 2 meta keys. They are relevant to my specific plugin, so I won’t go into details. Let’s build it!

First, we need to register our script and all block dependencies. In my code below, I am only using this sidebar for a custom post type, called grid_ad. I have also posted the entire code, without any optimizations, but if we have multiple similar meta keys, we can put the register_meta settings array into a variable, such as below:

$meta_key_settings = [
    'object_subtype'    => 'grid_ad',             // Custom post subtype
    'show_in_rest'      => true,                  // Allow the field to be edited in the REST API
    'single'            => true,                  // Only one value per post
    'type'              => 'string',              // Data type (string)
    'sanitize_callback' => 'sanitize_text_field', // Callback to sanitize input
];

Anyway, back to the code:

/**
 * Register sidebars for various post types
 *
 * @return void
 */
function wppd_register_sidebar() {
    // Register the JavaScript file for the sidebar
    wp_register_script(
        'my-sidebar-js', // Script handle
        plugins_url( 'assets/js/admin/sidebar.js', __FILE__ ), // Script source URL
        [
            'wp-plugins',    // Dependencies: WordPress plugins
            'wp-edit-post',  // Dependencies: WordPress edit post
            'wp-element',    // Dependencies: WordPress element
            'wp-components', // Dependencies: WordPress components
            'wp-data',       // Dependencies: WordPress data
        ]
    );

    // Register custom meta fields for the 'grid_ad' post type
    register_meta(
        'post',                  // Post type (in this case, 'post' is used for generic posts)
        'wppd_grid_ad_position', // Meta field name
        [
            'object_subtype'    => 'grid_ad',             // Custom post subtype
            'show_in_rest'      => true,                  // Allow the field to be edited in the REST API
            'single'            => true,                  // Only one value per post
            'type'              => 'string',              // Data type (string)
            'sanitize_callback' => 'sanitize_text_field', // Callback to sanitize input
        ]
    );

    // Register another custom meta field for the 'grid_ad' post type
    register_meta(
        'post',                 // Post type
        'wppd_grid_ad_columns', // Meta field name
        [
            'object_subtype'    => 'grid_ad',             // Custom post subtype
            'show_in_rest'      => true,                  // Allow the field to be edited in the REST API
            'single'            => true,                  // Only one value per post
            'type'              => 'number',              // Data type (number)
            'sanitize_callback' => 'sanitize_text_field', // Callback to sanitize input
        ]
    );
}

// Hook into WordPress 'init' action to register the sidebar
add_action( 'init', 'wppd_register_sidebar' );

/**
 * Enqueue the sidebar script for the block editor.
 */
function wppd_my_sidebar_script_enqueue() {
    // Get the current screen's information
    $screen = get_current_screen();

    // Check if the current post type is 'grid_ad'
    if ( 'grid_ad' === $screen->post_type ) {
        // Enqueue the JavaScript file for the sidebar
        wp_enqueue_script( 'my-sidebar-js' );
    }
}

// Hook into WordPress 'enqueue_block_editor_assets' action to enqueue the sidebar script
add_action( 'enqueue_block_editor_assets', 'wppd_my_sidebar_script_enqueue' );

Next, let’s create the actual sidebar.

Introduction and Dependencies

These are all the dependencies we are going to use for our sidebar. In the future, I intend to add a slider, some checkboxes, and a dropdown.

(function (wp) {
    // Import necessary dependencies from the WordPress block editor library
    const registerPlugin = wp.plugins.registerPlugin;
    const PluginSidebar = wp.editPost.PluginSidebar;
    const el = wp.element.createElement;
    const Text = wp.components.TextControl;
    const withSelect = wp.data.withSelect;
    const withDispatch = wp.data.withDispatch;
    const compose = wp.compose.compose;
})(window.wp);

Creating a MetaBlockField Component

let MetaBlockField = compose(
    withDispatch(function (dispatch, props) {
        return {
            setMetaFieldValue: function (value) {
                // Update the meta field value
                dispatch('core/editor').editPost({
                    meta: { [props.fieldName]: value },
                });
                // Then save the post (immediately), without the need for updating the post
                dispatch('core/editor').savePost();
            },
        };
    }),
    withSelect(function (select, props) {
        return {
            metaFieldValue: select('core/editor').getEditedPostAttribute('meta')[props.fieldName],
        };
    })
)(function (props) {
    return el(Text, {
        label: props.label,
        value: props.metaFieldValue,
        type: props.type, // 'text' or 'number'
        onChange: function (value) {
            // For number type, ensure the value is always a number or empty string
            if (props.type === 'number') {
                value = value === '' ? '' : Number(value);
            }
            props.setMetaFieldValue(value);
        },
    });
});

Registering the Plugin Sidebar

registerPlugin('wppd-sidebar--grid-ads', {
    render: function () {
        return el(
            PluginSidebar,
            {
                name: 'wppd-sidebar--grid-ads',
                icon: 'image-filter',
                title: 'Property Drive',
            },
            el(
                'div',
                { className: 'plugin-sidebar-content' },
                el(MetaBlockField, {
                    label: 'Grid Ad Order',
                    fieldName: 'wppd_grid_ad_position',
                    type: 'text',
                }),
                el(MetaBlockField, {
                    label: 'Grid Ad Columns',
                    fieldName: 'wppd_grid_ad_columns',
                    type: 'number',
                }),
            )
        );
    },
});

That’s all!

Now I can call my 2 meta keys in my plugin (or theme):

$grid_ad_position = (int) get_post_meta( $grid_ad_id, 'wppd_grid_ad_position', true );
$grid_ad_columns  = (int) get_post_meta( $grid_ad_id, 'wppd_grid_ad_columns', true );

I’m sure the code is self-explanatory

Related posts