The Gutenberg editor sidebar has two main tabs: Document (post-level settings: status, author, date, categories, featured image) and Block (current block settings). The Document panel is the natural home for custom post-level metadata that editors should always be able to access, regardless of which block they’ve selected. WordPress provides two JavaScript APIs for extending the Document panel: PluginDocumentSettingPanel adds a collapsible panel inside the Document sidebar, and PluginSidebar adds an entirely separate sidebar tab (covered elsewhere). For most use cases — a release date picker, a custom excerpt field, a featured label toggle, a post-specific CSS class — PluginDocumentSettingPanel is the correct choice because it keeps the setting alongside WordPress’s built-in post controls rather than hiding it in a separate tab that many editors will never discover. It uses the @wordpress/plugins and @wordpress/edit-post packages.
Problem: You have a release_year custom field for a movie post type and want editors to set it from the Gutenberg Document sidebar — alongside the standard Publish, Author, and Featured Image panels — rather than from a classic meta box at the bottom of the page.
Solution: Register the meta with register_post_meta() using 'show_in_rest' => true, then add a PluginDocumentSettingPanel that reads and writes the meta via the useEntityProp hook (WP 5.7+) or useSelect / useDispatch.
PHP — register the meta field and enqueue the editor script:
<?php
add_action( 'init', 'register_movie_meta' );
function register_movie_meta() {
register_post_meta( 'movie', 'release_year', [
'show_in_rest' => true,
'single' => true,
'type' => 'integer',
'default' => (int) date( 'Y' ),
'auth_callback' => function () {
return current_user_can( 'edit_posts' );
},
] );
}
add_action( 'enqueue_block_editor_assets', 'enqueue_movie_editor_panel' );
function enqueue_movie_editor_panel() {
if ( get_post_type() !== 'movie' ) return;
wp_enqueue_script(
'movie-editor-panel',
plugin_dir_url( __FILE__ ) . 'js/movie-panel.js',
[ 'wp-plugins', 'wp-edit-post', 'wp-element', 'wp-components', 'wp-data' ],
filemtime( plugin_dir_path( __FILE__ ) . 'js/movie-panel.js' )
);
}
JavaScript (js/movie-panel.js) — register the panel using useSelect / useDispatch:
const { registerPlugin } = wp.plugins;
const { PluginDocumentSettingPanel } = wp.editPost;
const { PanelRow, TextControl } = wp.components;
const { useSelect, useDispatch } = wp.data;
function MovieMetaPanel() {
// Read current meta value from the editor store
const releaseYear = useSelect( ( select ) => {
return select( 'core/editor' ).getEditedPostAttribute( 'meta' )?.release_year;
} );
// Get the function to edit post meta
const { editPost } = useDispatch( 'core/editor' );
return (
<PluginDocumentSettingPanel
name="movie-meta-panel"
title="Movie Details"
className="movie-meta-panel"
>
<PanelRow>
<TextControl
label="Release Year"
type="number"
value={ releaseYear || '' }
onChange={ ( value ) =>
editPost( { meta: { release_year: parseInt( value, 10 ) || 0 } } )
}
/>
</PanelRow>
</PluginDocumentSettingPanel>
);
}
registerPlugin( 'movie-meta-panel', { render: MovieMetaPanel } );
NOTE: The meta key must have 'show_in_rest' => true in its registration — without this, the Gutenberg editor's JavaScript data layer cannot read or write the value via the REST API, and your panel will silently fail to save changes. Also, PluginDocumentSettingPanel only appears in the sidebar when the Document tab is active — if you need the panel visible regardless of the selected tab, use PluginSidebar instead.