WordPress 6.7 introduced the PluginPreviewMenuItem component, and honestly, it is about time. For years, showing a user how their post would look on a social feed or in an email newsletter meant hacking together custom sidebars or modal triggers that lived in awkward places. There is now a proper extension point in the Preview dropdown.
In 14 years of wrestling with the WordPress editor I have seen plenty of half-baked APIs. This one follows the Slot/Fill pattern already used by components like PluginMoreMenuItem, so it is predictable to implement. If you have wanted a real-time social card preview that reflects unsaved edits, this is the tool for it.
Why PluginPreviewMenuItem matters for custom workflows
Before this, a custom preview meant stepping outside the native experience. Developers stuffed things into the “More Tools” menu or built persistent sidebars that pulled the author’s attention away from writing. PluginPreviewMenuItem puts your tool where a user would look for it, under the “Preview” button.
Because this runs inside the block editor’s React environment, you can reach for the useSelect hook and make the preview reactive. An author types a new title and the preview modal updates right then, before they touch “Save Draft.”
The PHP side: bootstrapping the assets
Like any Gutenberg extension, this starts with enqueuing scripts. Use the .asset.php file that @wordpress/scripts generates and let it handle dependencies for you. That is what keeps a missing core script from turning into a white screen.
<?php
/**
* Enqueue editor assets for the social preview plugin.
*/
function bbioon_enqueue_preview_extension() {
$asset_file = plugin_dir_path( __FILE__ ) . 'build/index.asset.php';
if ( ! file_exists( $asset_file ) ) {
return;
}
$asset = include $asset_file;
wp_enqueue_script(
'bbioon-social-preview',
plugin_dir_url( __FILE__ ) . 'build/index.js',
$asset['dependencies'],
$asset['version'],
true
);
}
add_action( 'enqueue_block_editor_assets', 'bbioon_enqueue_preview_extension' );
The JavaScript side: registering the menu item
The logic lives in src/index.js. registerPlugin from the @wordpress/plugins package injects your component into the Preview slot.
import { __ } from '@wordpress/i18n';
import { registerPlugin } from '@wordpress/plugins';
import { PluginPreviewMenuItem } from '@wordpress/editor';
import { useState } from '@wordpress/element';
import SocialPreviewModal from './components/SocialPreviewModal';
const SocialPreviewItem = () => {
const [ isOpen, setIsOpen ] = useState( false );
return (
<>
<PluginPreviewMenuItem
onClick={ () => setIsOpen( true ) }
>
{ __( 'Social Card Preview', 'bbioon' ) }
</PluginPreviewMenuItem>
{ isOpen && (
<SocialPreviewModal
onClose={ () => setIsOpen( false ) }
/>
) }
</>
);
};
registerPlugin( 'bbioon-social-preview', {
render: SocialPreviewItem,
} );
Reactive data fetching with useSelect
Pulling data from the core/editor store is where this gets useful. A standard frontend preview reads the database, but getEditedPostAttribute reads the current editor state instead. That matters for an author who wants to see how a featured image will look before committing the change.
If complex UI components have given you trouble before, my guide on choosing selection components may help with the rest of your plugin interface.
Fetching unsaved post data
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
const { title, excerpt } = useSelect( ( select ) => {
const { getEditedPostAttribute } = select( editorStore );
return {
title: getEditedPostAttribute( 'title' ),
excerpt: getEditedPostAttribute( 'excerpt' ),
};
}, [] );
I have used similar patterns when simplifying typography management in custom themes. Keep the selectors lightweight, or you will feel it as editor lag.
If PluginPreviewMenuItem work is eating your dev hours, I can take it on. I have been wrestling with WordPress since the 4.x days.
A more native editor experience
PluginPreviewMenuItem is part of WordPress getting more modular. It lets us build things like accessibility checkers or SEO simulators that feel like they belong in Core rather than bolted on beside it. There is a complete reference implementation in this official example repository.
So drop the hacky workarounds and use the official slots. The code stays maintainable, and nothing breaks on your clients when the next major WordPress release lands.