Gutenberg ships with a sidebar that shows two panels: Document (post settings, categories, tags) and Block (settings for the currently selected block). The block editor’s plugin API lets you add a third panel of your own — with any controls you choose — without touching the Document or Block panels or adding a classic meta box. This is useful for editorial workflows that need additional fields in the editor: reading-time estimates, internal notes, SEO scores, approval status flags, or any custom post meta. The mechanism is the PluginSidebar component from @wordpress/edit-post, combined with registerPlugin() from @wordpress/plugins. The sidebar entry also automatically appears in the editor’s three-dot Options menu under a Plugins section so the author can open or close it at any time. The underlying data — whatever you put in the sidebar — is stored as post meta and accessed through WordPress’s REST API data layer, which means you must register the meta field with register_post_meta() and set show_in_rest to true. All of this works without a build step: the example below uses wp.element.createElement (the WordPress-bundled version of React) rather than JSX, so it runs as a plain .js file enqueued with enqueue_block_editor_assets.
Problem: You need to expose additional fields — post meta, internal notes, or custom controls — in the Gutenberg editor without adding a classic meta box below the content area or cluttering the existing Document panel.
Solution: Register a custom plugin with registerPlugin() and render a PluginSidebar inside it. Expose the post meta field via register_post_meta() with show_in_rest: true so the JavaScript data layer can read and write it through the REST API.
Step 1 — enqueue the sidebar script only in the block editor (not on the front end):
<?php
add_action( 'enqueue_block_editor_assets', 'register_custom_sidebar_script' );
function register_custom_sidebar_script() {
wp_enqueue_script(
'my-custom-sidebar',
get_template_directory_uri() . '/js/editor-sidebar.js',
[ 'wp-plugins', 'wp-edit-post', 'wp-element', 'wp-components', 'wp-data' ],
filemtime( get_template_directory() . '/js/editor-sidebar.js' ),
true
);
}
add_action( 'init', 'register_reading_notes_meta' );
function register_reading_notes_meta() {
register_post_meta( 'post', '_reading_notes', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function () {
return current_user_can( 'edit_posts' );
},
] );
}
Step 2 — the sidebar JavaScript (js/editor-sidebar.js). Uses withSelect / withDispatch to connect the textarea to the post meta store:
( function () {
var el = wp.element.createElement;
var Fragment = wp.element.Fragment;
var PluginSidebar = wp.editPost.PluginSidebar;
var PluginSidebarMoreMenuItem = wp.editPost.PluginSidebarMoreMenuItem;
var registerPlugin = wp.plugins.registerPlugin;
var TextareaControl = wp.components.TextareaControl;
var withSelect = wp.data.withSelect;
var withDispatch = wp.data.withDispatch;
var SidebarContent = withSelect( function ( select ) {
return { meta: select( 'core/editor' ).getEditedPostAttribute( 'meta' ) };
} )( withDispatch( function ( dispatch ) {
return {
updateMeta: function ( value ) {
dispatch( 'core/editor' ).editPost( { meta: { _reading_notes: value } } );
},
};
} )( function ( props ) {
return el( TextareaControl, {
label: 'Reading Notes',
value: props.meta._reading_notes || '',
onChange: props.updateMeta,
rows: 6,
} );
} ) );
registerPlugin( 'my-custom-sidebar', {
render: function () {
return el(
Fragment, null,
el( PluginSidebarMoreMenuItem, { target: 'my-custom-sidebar' }, 'Reading Notes' ),
el( PluginSidebar, { name: 'my-custom-sidebar', title: 'Reading Notes' },
el( SidebarContent, null )
)
);
},
} );
} )();
NOTE: The withSelect / withDispatch higher-order components shown above were the standard pattern in WordPress 5.x (late 2019). In modern projects you will typically see the equivalent useSelect and useDispatch React hooks — they are functionally identical but require a JSX build step. The plain createElement approach used here runs without any build tooling and is a good starting point before adding a webpack or @wordpress/scripts build pipeline.