{"id":5862,"date":"2026-02-19T14:30:31","date_gmt":"2026-02-19T14:30:31","guid":{"rendered":"https:\/\/developer.wordpress.org\/news\/?p=5862"},"modified":"2026-02-19T14:30:31","modified_gmt":"2026-02-19T14:30:31","slug":"how-to-add-custom-entries-to-the-editor-preview-dropdown","status":"publish","type":"post","link":"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/","title":{"rendered":"How to add custom entries to the editor Preview dropdown"},"content":{"rendered":"\n<p class=\"has-manrope-font-family wp-block-paragraph\"><a href=\"https:\/\/make.wordpress.org\/core\/2024\/10\/18\/extending-the-preview-dropdown-menu-in-wordpress-6-7\/\">Since WordPress 6.7<\/a>, plugin developers can extend the Preview dropdown in the editor with custom menu items. This is possible through the <code><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/packages\/packages-editor\/#pluginpreviewmenuitem\">PluginPreviewMenuItem<\/a><\/code> component from the <code>@wordpress\/editor<\/code> package. This article walks through building a small plugin that adds a \u201cSocial Card Preview\u201d option \u2014 showing how a post will look when shared on X.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;69e20002a3d42&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"69e20002a3d42\" class=\"wp-block-image size-full wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"922\" height=\"373\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-16.20.58.png\" alt=\"Screenshot of a Preview Menu \" class=\"wp-image-5863\" srcset=\"https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-16.20.58.png 922w, https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-16.20.58-300x121.png 300w, https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-16.20.58-768x311.png 768w\" sizes=\"auto, (max-width: 922px) 100vw, 922px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<div class=\"wp-block-group has-light-grey-2-background-color has-background is-layout-flow wp-block-group-is-layout-flow\" style=\"border-radius:2px;margin-top:var(--wp--preset--spacing--30);margin-bottom:var(--wp--preset--spacing--30);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)\">\n<p class=\"has-large-font-size wp-block-paragraph\" style=\"font-style:normal;font-weight:600;line-height:1\">Table of Contents<\/p>\n\n\n\n<nav aria-label=\"Table of Contents\" class=\"wp-block-table-of-contents\"><ol><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#what-is-pluginpreviewmenuitem\">What is PluginPreviewMenuItem?<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#social-card-preview-example\">Social Card Preview example<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#plugin-setup\">Plugin setup<\/a><ol><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#the-php-bootstrap\">The PHP bootstrap<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#build-tooling\">Build tooling<\/a><\/li><\/ol><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#registering-the-preview-menu-item\">Registering the preview menu item<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#building-the-social-card-modal\">Building the social card modal<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#the-card-preview-markup\">The Card Preview markup<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#styling-the-card\">Styling the card<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#testing-with-wordpress-playground\">Testing with WordPress Playground<\/a><\/li><li><a class=\"wp-block-table-of-contents__entry\" href=\"https:\/\/developer.wordpress.org\/news\/2026\/02\/how-to-add-custom-entries-to-the-editor-preview-dropdown\/#going-further\">Going further<\/a><\/li><\/ol><\/nav>\n<\/div>\n\n\n\n<h2 id=\"what-is-pluginpreviewmenuitem\" class=\"wp-block-heading\">What is PluginPreviewMenuItem?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <strong>Preview<\/strong> dropdown sits in the editor&#8217;s top bar and lets users preview their content in different ways. Before WordPress 6.7, only Core add items there. With <code>PluginPreviewMenuItem<\/code> component, plugins can register their own entries in that dropdown. It uses the same <a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/components\/slot-fill\/\">Slot\/Fill pattern<\/a> that powers other extension points like <code>PluginMoreMenuItem<\/code> or <code>PluginSidebar<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The component accepts an <code>onClick<\/code> handler for button behavior or an <code>href<\/code> for link behavior. You can also pass an optional <code>icon<\/code> prop. The text you place between the opening and closing <code>&lt;PluginPreviewMenuItem&gt;<\/code> tags becomes the label shown in the menu.<\/p>\n\n\n\n<h2 id=\"social-card-preview-example\" class=\"wp-block-heading\">Social Card Preview example<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This plugin adds a <strong>Social Card Preview<\/strong> item to the <strong>Preview<\/strong> dropdown. When clicked, it opens a modal that shows a mock preview of how the current post would appear as an X card. The modal reads the post title, excerpt, and featured image directly from the editor store, so it always reflects the latest unsaved edits. You can take a peek at the <a href=\"https:\/\/github.com\/bph\/dev-blog-custom-preview\">example plugin on GitHub. <\/a><\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;69e20002a5997&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"69e20002a5997\" class=\"wp-block-image size-full wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"973\" height=\"708\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-13.47.22.png\" alt=\"Screenshot of Preview displayed in a modal\" class=\"wp-image-5864\" srcset=\"https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-13.47.22.png 973w, https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-13.47.22-300x218.png 300w, https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/Screenshot-2026-02-18-at-13.47.22-768x559.png 768w\" sizes=\"auto, (max-width: 973px) 100vw, 973px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<h2 id=\"plugin-setup\" class=\"wp-block-heading\">Plugin setup<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Following along, you start with the file structure. The plugin needs a main PHP file for WordPress to recognize it, a <code>package.json<\/code> for the build tooling, and the source files in a <code>src\/<\/code> folder.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"markdown\" class=\"language-markdown\">custom-preview\/\n\u251c\u2500\u2500 custom-preview.php\n\u251c\u2500\u2500 package.json\n\u2514\u2500\u2500 src\/\n    \u251c\u2500\u2500 index.js\n    \u251c\u2500\u2500 social-card-preview.js\n    \u2514\u2500\u2500 style.css<\/code><\/pre>\n\n\n\n<h3 id=\"the-php-bootstrap\" class=\"wp-block-heading\">The PHP bootstrap<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The main plugin file, <code>custom-preview.php<\/code>, registers the script and stylesheet on the <code>enqueue_block_editor_assets<\/code> hook. The code uses the asset file that <code>@wordpress\/scripts<\/code> generates during the build \u2014 it contains the dependency list and a version hash so WordPress can handle caching properly. The below code goes after the <a href=\"https:\/\/developer.wordpress.org\/plugins\/plugin-basics\/header-requirements\/\">PHP plugin header<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code lang=\"php\" class=\"language-php\">function social_card_preview_enqueue_editor_assets() {\n\t$asset_file = plugin_dir_path( __FILE__ ) . 'build\/index.asset.php';\n\n\tif ( ! file_exists( $asset_file ) ) {\n\t\treturn;\n\t}\n\n\t$asset = include $asset_file;\n\n\twp_enqueue_script(\n\t\t'social-card-preview-editor',\n\t\tplugin_dir_url( __FILE__ ) . 'build\/index.js',\n\t\t$asset['dependencies'],\n\t\t$asset['version'],\n\t\ttrue\n\t);\n\n\twp_enqueue_style(\n\t\t'social-card-preview-editor',\n\t\tplugin_dir_url( __FILE__ ) . 'build\/style-index.css',\n\t\tarray(),\n\t\t$asset['version']\n\t);\n}\nadd_action( 'enqueue_block_editor_assets', 'social_card_preview_enqueue_editor_assets' );<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is a common pattern in WordPress block editor plugin development. The <code>enqueue_block_editor_assets<\/code> hook makes sure <a href=\"https:\/\/developer.wordpress.org\/block-editor\/how-to-guides\/enqueueing-assets-in-the-editor\/#editor-scripts-and-styles\">the script and styles only load inside the editor<\/a>, not on the frontend. <\/p>\n\n\n\n<h3 id=\"build-tooling\" class=\"wp-block-heading\">Build tooling<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For the JavaScript build, <code>@wordpress\/scripts<\/code> handles the tooling. The <code>package.json<\/code> is minimal:<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code lang=\"json\" class=\"language-json\">{\n\t\"name\": \"social-card-preview\",\n\t\"version\": \"1.0.0\",\n\t\"scripts\": {\n\t\t\"build\": \"wp-scripts build\",\n\t\t\"start\": \"wp-scripts start\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@wordpress\/scripts\": \"^30.0.0\"\n\t}\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run <code>npm install<\/code> to get the dependencies, then <code>npm run build<\/code> to compile. During development, <code>npm run start<\/code> watches for changes and rebuilds automatically.<\/p>\n\n\n\n<h2 id=\"registering-the-preview-menu-item\" class=\"wp-block-heading\">Registering the preview menu item<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The entry point <code>src\/index.js<\/code> is where the plugin is registered and the menu item is added to the Preview dropdown. The idea behind it: render a <code>PluginPreviewMenuItem<\/code> that, when clicked, opens a modal.<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code lang=\"jsx\" class=\"language-jsx line-numbers\">import { __ } from '@wordpress\/i18n';\nimport { registerPlugin } from '@wordpress\/plugins';\nimport { PluginPreviewMenuItem } from '@wordpress\/editor';\nimport { useState } from '@wordpress\/element';\n\nimport SocialCardPreview from '.\/social-card-preview';\nimport '.\/style.css';\n\nconst SocialCardPreviewMenuItem = () =&gt; {\n\tconst [ isOpen, setIsOpen ] = useState( false );\n\n\treturn (\n\t\t&lt;&gt;\n\t\t\t&lt;PluginPreviewMenuItem\n\t\t\t\tonClick={ () =&gt; setIsOpen( true ) }\n\t\t\t&gt;\n\t\t\t\t{ __( 'Social Card Preview', 'social-card-preview' ) }\n\t\t\t&lt;\/PluginPreviewMenuItem&gt;\n\t\t\t{ isOpen &amp;&amp; (\n\t\t\t\t&lt;SocialCardPreview\n\t\t\t\t\tonClose={ () =&gt; setIsOpen( false ) }\n\t\t\t\t\/&gt;\n\t\t\t) }\n\t\t&lt;\/&gt;\n\t);\n};\n\nregisterPlugin( 'social-card-preview', {\n\trender: SocialCardPreviewMenuItem,\n} );<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A few things to note here: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The code uses <code>registerPlugin<\/code> from <code><a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/packages\/packages-plugins\/\">@wordpress\/plugins<\/a><\/code> to register the component. <\/li>\n\n\n\n<li>The <code>PluginPreviewMenuItem<\/code> works like any other menu item \u2014 give it an <code>onClick<\/code> and some children for the label. <\/li>\n\n\n\n<li>Modal visibility is managed with a basic <code>useState<\/code> toggle.<\/li>\n<\/ul>\n\n\n\n<h2 id=\"building-the-social-card-modal\" class=\"wp-block-heading\">Building the social card modal<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>SocialCardPreview<\/code> component reads data from the editor store and renders a mock X card inside a <code>Modal<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">import { __ } from '@wordpress\/i18n';\nimport { Modal } from '@wordpress\/components';\nimport { useSelect } from '@wordpress\/data';\nimport { store as editorStore } from '@wordpress\/editor';\nimport { store as coreStore } from '@wordpress\/core-data';\n\nconst SocialCardPreview = ( { onClose } ) =&gt; {\n\tconst { title, excerpt, imageUrl, siteUrl } = useSelect( ( select ) =&gt; {\n\t\tconst { getEditedPostAttribute } = select( editorStore );\n\t\tconst featuredMediaId = getEditedPostAttribute( 'featured_media' );\n\n\t\tlet featuredImageUrl = '';\n\t\tif ( featuredMediaId ) {\n\t\t\tconst media = select( coreStore ).getMedia( featuredMediaId );\n\t\t\tfeaturedImageUrl =\n\t\t\t\tmedia?.media_details?.sizes?.large?.source_url ||\n\t\t\t\tmedia?.source_url ||\n\t\t\t\t'';\n\t\t}\n\n\t\treturn {\n\t\t\ttitle: getEditedPostAttribute( 'title' ) || '',\n\t\t\texcerpt: getEditedPostAttribute( 'excerpt' ) || '',\n\t\t\timageUrl: featuredImageUrl,\n\t\t\tsiteUrl: select( coreStore ).getSite()?.url || '',\n\t\t\t\t\t};\n\t}, [] );\n\n\tconst domain = siteUrl ? new URL( siteUrl ).hostname : '';\n\tconst truncatedExcerpt =\n\t\texcerpt.length &gt; 200\n\t\t\t? excerpt.substring( 0, 200 ) + '\u2026'\n\t\t\t: excerpt;\n\n\treturn (\n\t\t&lt;Modal title={ __( 'X Preview', 'social-card-preview' ) } \n                       onRequestClose={ onClose } \n                       size=\"medium\"&gt;\n\t\t\t{ \/* Card preview markup see below *\/ }\n\t\t&lt;\/Modal&gt;\n\t);\n};<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a breakdown. The <code>useSelect<\/code> hook pulls the required data from two stores. From the <code>editor<\/code> store, it retrieves the post title, excerpt, and featured media ID. From the <code>core-data<\/code> store, it resolves the media ID to an actual image URL and fetches the site URL to display the domain name on the card.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the featured image, the code tries the <code>large<\/code> size first, falling back to the full <code>source_url<\/code>. The excerpt gets truncated to 200 characters because that is roughly what X shows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because <code>useSelect<\/code> is reactive, the preview updates in real time as you edit the post. Change the title, and the card updates immediately \u2014 no need to save first.<\/p>\n\n\n\n<h2 id=\"the-card-preview-markup\" class=\"wp-block-heading\">The Card Preview markup<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The card markup mirrors how X renders link previews. The featured image sits at the top, followed by a content area with the domain, title, and truncated excerpt. The image only renders when a featured image exists, thanks to the conditional <code>imageUrl &amp;&amp;<\/code> check.&nbsp; <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Each piece of text uses a <code>&lt;span&gt;<\/code> rather than a heading or paragraph because&nbsp;this is a visual mock \u2014 not semantic document content. The class names like <code>social-card-preview__title<\/code> follow the <a href=\"https:\/\/getbem.com\/\">BEM (Block Element Modifier)<\/a> naming&nbsp;convention \u2014 <code>social-card-preview<\/code> is the block, and <code>__title<\/code>, <code>__domain<\/code>,&nbsp; <code>__description<\/code> are elements within it. This keeps the styles scoped and avoids collisions with editor styles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The below text goes between the <code>&lt;Modal&gt;&lt;\/Modal&gt;<\/code> tags in above code example. The complete <code><a href=\"https:\/\/github.com\/bph\/dev-blog-custom-preview\/blob\/main\/src\/social-card-preview.js\">social-card-preview.js<\/a><\/code> is also on GitHub:<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code lang=\"jsx\" class=\"language-jsx line-numbers\">&lt;div className=\"social-card-preview\"&gt;\n    &lt;div className=\"social-card-preview__card social-card-preview__card--twitter\"&gt;\n\t{ imageUrl &amp;&amp; (\n\t\t&lt;img className=\"social-card-preview__image\"\n\t\t\tsrc={ imageUrl }\n\t\t\talt=\"\"\n\t\t\/&gt;\n\t) }\n      &lt;div className=\"social-card-preview__content\"&gt;\n\t  &lt;span className=\"social-card-preview__domain\"&gt;\n\t\t{ domain }\n\t  &lt;\/span&gt;\n          &lt;span className=\"social-card-preview__title\"&gt;\n\t       { title }\n         &lt;\/span&gt;\n         &lt;span className=\"social-card-preview__description\"&gt;\n\t         { truncatedExcerpt }\n         &lt;\/span&gt;\n     &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<h2 id=\"styling-the-card\" class=\"wp-block-heading\">Styling the card<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The CSS approximates how X renders link cards and follows the naming decision from above. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"css\" class=\"language-css\">.social-card-preview__card {\n\tborder: 1px solid #dadce0;\n\tborder-radius: 16px;\n\toverflow: hidden;\n}\n\n.social-card-preview__title {\n\tdisplay: -webkit-box;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n\toverflow: hidden;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The card uses a 16px border radius to match X\u2019s rounded style. The <code>-webkit-line-clamp<\/code> rules handle text truncation for the title and description.<\/p>\n\n\n\n<h2 id=\"testing-with-wordpress-playground\" class=\"wp-block-heading\">Testing with WordPress Playground<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You do not need a full WordPress installation to test this. The <a href=\"https:\/\/wordpress.github.io\/wordpress-playground\/developers\/local-development\/wp-playground-cli\/\">Playground CLI<\/a> gives you a disposable WordPress instance in seconds. Make sure you have Node.js 20.18 or later, then from the plugin directory run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">npm install\nnpm run build\nnpx @wp-playground\/cli@latest server --auto-mount --login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Playground detects the plugin, automatically mounts it and logs in the admin user. Open <code>http:\/\/127.0.0.1:9400<\/code> in your browser, create or edit a post, and you should see \u201cSocial Card Preview\u201d in the Preview dropdown.<\/p>\n\n\n\n<h2 id=\"going-further\" class=\"wp-block-heading\">Going further<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The social card example is one way to use <code>PluginPreviewMenuItem<\/code>. The slot opens up a range of preview experiences that were previously only available to WordPress core. Here are some other use cases to consider:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Accessibility checker<\/strong> \u2014 open a modal that scans the post content for common accessibility issues, such as missing alt text or low-contrast headings, before the author hits publish.<\/li>\n\n\n\n<li><strong>Reading level preview<\/strong> \u2014 show a readability score and a simplified version of the content, useful for publishers targeting a broad or non-specialist audience.<\/li>\n\n\n\n<li><strong>External preview service<\/strong> \u2014 use the <code>href<\/code> prop instead of <code>onClick<\/code> to send the author directly to a third-party staging or QA environment with the post URL pre-filled.<\/li>\n\n\n\n<li><strong>Email newsletter preview<\/strong> \u2014 for plugins that sync posts to an email list, show a rendered preview of how the content will look in an email client, including any template wrapping applied by the plugin.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Any time your plugin needs to give authors a way to see or check their content before publishing, <code>PluginPreviewMenuItem<\/code> is the right place to add it.<\/p>\n\n\n\n<p class=\"has-text-align-right wp-block-paragraph\"><em>Props to <\/em><a href='https:\/\/profiles.wordpress.org\/greenshady\/' class='mention'><span class='mentions-prefix'>@<\/span>greenshady<\/a><em>and <\/em><a href='https:\/\/profiles.wordpress.org\/juanmaguitar\/' class='mention'><span class='mentions-prefix'>@<\/span>juanmaguitar<\/a> <em>for reviews.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how plugin developers can extend the editor&#8217;s Preview dropdown with custom menu items using the PluginPreviewMenuItem component. <\/p>\n","protected":false},"author":5713323,"featured_media":5869,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"edge","default_image_id":0,"font":"","enabled":false},"version":2},"_wpas_customize_per_network":false},"categories":[37,113,40],"tags":[218,95],"class_list":["post-5862","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-beginners","category-common-apis","category-plugins","tag-block-editor","tag-editor-curation","mentions-greenshady","mentions-juanmaguitar"],"revision_note":"","jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/developer.wordpress.org\/news\/files\/2026\/02\/custom-preview.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/posts\/5862","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/users\/5713323"}],"replies":[{"embeddable":true,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/comments?post=5862"}],"version-history":[{"count":18,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/posts\/5862\/revisions"}],"predecessor-version":[{"id":5886,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/posts\/5862\/revisions\/5886"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/media\/5869"}],"wp:attachment":[{"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/media?parent=5862"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/categories?post=5862"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/developer.wordpress.org\/news\/wp-json\/wp\/v2\/tags?post=5862"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}