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.

Leave a Reply