How to edit post meta outside of a Gutenberg Block?

The Block API of Gutenberg supports post meta as a attribute source. But what if you want to use post meta in say a plugin sidebar?

With the help of the @wordpress/data package and so called “higher-order components” it’s as easy as for blocks. 🙌

First we import all the required dependencies. This assumes that you’re using webpack and define your externals like this.

/**
 * WordPress dependencies
 */
import {
	withSelect,
	withDispatch,
} from '@wordpress/data';
import {
	PluginSidebar,
	PluginSidebarMoreMenuItem,
} from '@wordpress/edit-post';
import {
	PanelColor,
} from '@wordpress/editor';
import {
	Component,
	Fragment,
	compose,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { registerPlugin } from '@wordpress/plugins';

To render the UI we extend Component and implement the render() method. For this example I used a PanelColor component which allows a user to select a color. We’ll save the hex value in the post meta my_color_one:

/**
 * Custom component with a simple color panel.
 */
class MyPlugin extends Component {

	render() {
		// Nested object destructuring.
		const {
			meta: {
				my_color_one: colorOne,
			} = {},
			updateMeta,
		} = this.props;

		return (
			<Fragment>
				<PluginSidebarMoreMenuItem
					name="my-plugin-sidebar"
					type="sidebar"
					target="my-plugin-sidebar"
				>
					{ __( 'Color it!', 'my-plugin' ) }
				</PluginSidebarMoreMenuItem>
				<PluginSidebar
					name="my-plugin-sidebar"
					title={ __( 'Color it!', 'my-plugin' ) }
				>
					<PanelColor
						colorValue={ colorOne}
						initialOpen={ false }
						title={ __( 'Color 1', 'my-plugin' ) }
						onChange={ ( value ) => {
							// value is undefined if color is cleared.
							updateMeta( { my_color_one: value || '' } );
						} }
					/>
				</PluginSidebar>
			</Fragment>
		);
	}
}

(Please ignore the incorrect syntax highlighting.)

Now we need a higher-order component which is used to fetch the data, in this case our post meta.

// Fetch the post meta.
const applyWithSelect = withSelect( ( select ) => {
	const { getEditedPostAttribute } = select( 'core/editor' );

	return {
		meta: getEditedPostAttribute( 'meta' ),
	};
} );

The second higher-order component is used to update the data.

// Provide method to update post meta.
const applyWithDispatch = withDispatch( ( dispatch, { meta } ) => {
	const { editPost } = dispatch( 'core/editor' );

	return {
		updateMeta( newMeta ) {
			editPost( { meta: { ...meta, ...newMeta } } ); // Important: Old and new meta need to be merged in a non-mutating way!
		},
	};
} );

Since we now have two higher-order components we have to combine them with compose:

// Combine the higher-order components.
const render = compose( [
	applyWithSelect,
	applyWithDispatch
] )( MyPlugin );

Having a fully renderable component we can finally register our custom plugin:

registerPlugin( 'my-plugin', {
	icon: 'art',
	render,
} );

And that’s it! 🚢

PS: Make sure to register the post meta properly with 'show_in_rest' => true.


Photo by Kimberly Farmer.

2 thoughts on “How to edit post meta outside of a Gutenberg Block?”

  1. I’m having an excessively hard time locating any functional walkthroughs or docs on how to use the plugin API in 5.0 and this is the only thing I’ve been able to locate that actually walks through making it all work. Thank you so much stranger!

Leave a Reply