{"id":4966,"date":"2026-05-02T19:23:14","date_gmt":"2026-05-02T19:23:14","guid":{"rendered":"https:\/\/frontlinenewsng.org\/?p=4966"},"modified":"2026-05-02T19:23:14","modified_gmt":"2026-05-02T19:23:14","slug":"gutenberg-times-block-format-bridge-a-practical-solution-for-ai-generated-content-in-wordpress","status":"publish","type":"post","link":"https:\/\/frontlinenewsng.org\/?p=4966","title":{"rendered":"Gutenberg Times: Block Format Bridge: A Practical Solution for AI-Generated Content in WordPress"},"content":{"rendered":"<p class=\"wp-block-paragraph\"><a href=\"https:\/\/chubes.net\/\">Chris Huber,<\/a> developer at Automattic, released <a href=\"https:\/\/github.com\/chubes4\/block-format-bridge\">Block Format Bridge<\/a>, an open-source plugin that addresses one of the more persistent friction points in AI-assisted WordPress workflows: getting AI-generated content into the block editor reliably.<\/p>\n<p class=\"wp-block-paragraph\">The plugin takes a pragmatic approach. Block markup is notoriously difficult for AI to produce correctly \u2014 not because AI models lack capability, but because of how the format works. As Dennis Snell explained back in 2017 in his still-essential post <a href=\"https:\/\/fluffyandflakey.blog\/2017\/09\/04\/gutenberg-posts-arent-html\/\">Gutenberg posts aren\u2019t HTML<\/a>, a Gutenberg post is a serialized tree structure that happens to be stored as HTML with JSON-carrying comment delimiters. It was never designed to be written by hand \u2014 or by an AI inferring its way through a <code>save()<\/code> function it can\u2019t actually execute. The result, for anyone building publishing automations, REST API integrations, or agent workflows that call <code>wp_insert_post()<\/code>, is a familiar failure mode: content that saves fine, then opens in the editor with invalid blocks or silently falls back to the classic editor.<\/p>\n<p class=\"wp-block-paragraph\">Even a block as common as a styled quote illustrates the problem:<\/p>\n<blockquote class=\"wp-block-quote is-style-large is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">The generated HTML should be treated as throwaway code.<\/p>\n<p><cite>Dennis Snell<\/cite><\/p><\/blockquote>\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\"><span>JSON<\/span><span class=\"code-block-pro-copy-button\">\n<pre class=\"code-block-pro-copy-button-pre\"><textarea class=\"code-block-pro-copy-button-textarea\" readonly>&lt;!-- wp:quote {\"className\":\"is-style-large\"} --&gt;\n&lt;blockquote class=\"wp-block-quote is-style-large\"&gt;\n    &lt;p&gt;The generated HTML should be treated as throwaway code.&lt;\/p&gt;\n    &lt;cite&gt;Dennis Snell&lt;\/cite&gt;\n&lt;\/blockquote&gt;\n&lt;!-- \/wp:quote --&gt;<\/textarea><\/pre>\n<p><\/p><\/span>\n<pre class=\"shiki rose-pine-dawn\"><code><span class=\"line\"><span>&lt;!-- wp:quote <\/span><span>{<\/span><span>\"<\/span><span>className<\/span><span>\"<\/span><span>:<\/span><span>\"is-style-large\"<\/span><span>}<\/span><span> --&gt;<\/span><\/span>\n<span class=\"line\"><span>&lt;blockquote class=<\/span><span>\"wp-block-quote is-style-large\"<\/span><span>&gt;<\/span><\/span>\n<span class=\"line\"><span>    &lt;p&gt;The generated HTML should be treated as throwaway code.&lt;\/p&gt;<\/span><\/span>\n<span class=\"line\"><span>    &lt;cite&gt;Dennis Snell&lt;\/cite&gt;<\/span><\/span>\n<span class=\"line\"><span>&lt;\/blockquote&gt;<\/span><\/span>\n<span class=\"line\"><span>&lt;!-- \/wp:quote --&gt;<\/span><\/span><\/code><\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">The <code>className<\/code> attribute in the comment has to match the class on the HTML element. The <code>cite<\/code> tag must follow the exact structure the block\u2019s <code>save()<\/code> function produces. Get either wrong and the block is invalid \u2014 and with more complex blocks like <code>wp:cover<\/code> or <code>wp:columns<\/code>, the surface area for errors grows considerably.<\/p>\n<h2 class=\"wp-block-heading\">HTML to Blocks converter and vice versa<\/h2>\n<p class=\"wp-block-paragraph\">Block Format Bridge sidesteps the problem by letting AI output what it does well \u2014 Markdown or plain HTML \u2014 and handling the conversion to block markup server-side, using established PHP libraries. It builds on <a href=\"https:\/\/github.com\/chubes4\/html-to-blocks-converter\"><code>chubes4\/html-to-blocks-converter<\/code><\/a> for the write side, WordPress core\u2019s <code>do_blocks()<\/code> for rendering, and <code>league\/commonmark<\/code> and <code>league\/html-to-markdown<\/code> for Markdown support.<\/p>\n<p class=\"wp-block-paragraph\">The core API is compact and readable:<\/p>\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\"><span>JSON<\/span><span class=\"code-block-pro-copy-button\">\n<pre class=\"code-block-pro-copy-button-pre\"><textarea class=\"code-block-pro-copy-button-textarea\" readonly>\/ Markdown \u2192 blocks\n$blocks = bfb_convert( \"# HellonnSome content here.\", 'markdown', 'blocks' );\n\n\/ HTML \u2192 blocks\n$blocks = bfb_convert( '&lt;h1&gt;Hello&lt;\/h1&gt;&lt;p&gt;Some content here.&lt;\/p&gt;', 'html', 'blocks' );\n\n\/ Blocks \u2192 Markdown (for reading back to AI)\n$md = bfb_render_post( $post_id, 'markdown' );<\/textarea><\/pre>\n<p><\/p><\/span>\n<pre class=\"shiki rose-pine-dawn\"><code><span class=\"line\"><span>\/ Markdown \u2192 blocks<\/span><\/span>\n<span class=\"line\"><span>$blocks = bfb_convert( <\/span><span>\"# Hello<\/span><span>nn<\/span><span>Some content here.\"<\/span><span>, 'markdown', 'blocks' );<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span>\/ HTML \u2192 blocks<\/span><\/span>\n<span class=\"line\"><span>$blocks = bfb_convert( '&lt;h<\/span><span>1<\/span><span>&gt;Hello&lt;\/h<\/span><span>1<\/span><span>&gt;&lt;p&gt;Some content here.&lt;\/p&gt;', 'html', 'blocks' );<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span>\/ Blocks \u2192 Markdown (for reading back to AI)<\/span><\/span>\n<span class=\"line\"><span>$md = bfb_render_post( $post_id, 'markdown' );<\/span><\/span><\/code><\/pre>\n<\/div>\n<p class=\"wp-block-paragraph\">It also adds a <code>?content_format=<\/code> query parameter to the REST API, so AI agents can fetch existing post content as Markdown \u2014 not raw block markup \u2014 which makes edit workflows considerably more reliable.<\/p>\n<p class=\"wp-block-paragraph\">The architecture is extensible. New formats can be added by registering a new adapter without touching the core bridge, and the <code>bfb_default_format<\/code> filter lets you declare that a custom post type writes in Markdown by default, so any code path calling <code>wp_insert_post()<\/code> gets the same conversion behavior automatically.<\/p>\n<h2 class=\"wp-block-heading\">Does This Need a Skill?<\/h2>\n<p class=\"wp-block-paragraph\">After sharing an early draft of this post with Chris Huber, he offered a perspective worth sitting with: this plugin is designed to <em>eliminate<\/em> a skill rather than add one.<\/p>\n<p class=\"wp-block-paragraph\">When Block Format Bridge is bundled as a dependency and the system prompt simply instructs the agent to insert post content as Markdown, the AI doesn\u2019t need to know the plugin exists at all. A single line \u2014 <em>\u201cpost content should be inserted as Markdown\u201d<\/em> \u2014 is enough. The conversion happens automatically, invisibly, in PHP. The complexity disappears into infrastructure rather than into instructions.<\/p>\n<p class=\"wp-block-paragraph\">That\u2019s a different philosophy from agent-skills, which is about making AI <em>aware<\/em> of patterns and tools. The more elegant approach here is the opposite: good tooling that makes the AI less aware, not more. An end user of a plugin built on top of Block Format Bridge would never know it exists \u2014 they\u2019d just see valid blocks in the editor.<\/p>\n<p class=\"wp-block-paragraph\">A skill may still have a role for developers who don\u2019t control the system prompt and need to guide agent behavior through other means. But for anyone building AI-powered WordPress plugins or automations, the cleaner pattern is to bundle the plugin, set the default format, and let the infrastructure do its job.<\/p>\n<p class=\"wp-block-paragraph\">A draft skill is available below for those who do want to experiment with the agent-skills approach.<\/p>\n<p class=\"wp-block-paragraph\">A draft skill can be downloaded to use the Block Format Bridge . <\/p>\n<div class=\"wp-block-file\"><a href=\"https:\/\/gutenbergtimes.com\/wp-content\/uploads\/2026\/04\/wp-block-content-skill.zip\">wp-block-content-skill<\/a><a class=\"wp-block-file__button wp-element-button\" href=\"https:\/\/gutenbergtimes.com\/wp-content\/uploads\/2026\/04\/wp-block-content-skill.zip\">Download<\/a><\/div>\n<p class=\"wp-block-paragraph\"> <img data-opt-id=1244057809  fetchpriority=\"high\" data-recalc-dims=\"1\" decoding=\"async\" alt=\"\ud83d\udc32\" class=\"wp-smiley\" src=\"https:\/\/mlcqvjhyzqda.i.optimole.com\/cb:UNMm.7bb\/w:auto\/h:auto\/q:mauto\/f:best\/https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f432.png?w=640&#038;ssl=1\" \/> All is still a work in progress so there might be dragons<\/p>\n<p class=\"wp-block-paragraph\"><em>As a small footnote, this post was drafted with AI assistance and had to be converted to blocks before I could edit it. \u2014which felt fitting given the subject<\/em><\/p>","protected":false},"excerpt":{"rendered":"<p>Chris Huber, developer at Automattic, released Block Format Bridge, an open-source plugin that addresses one of the more persistent friction points in AI-assisted WordPress workflows: getting AI-generated content into the block editor reliably. The plugin takes a pragmatic approach. Block markup is notoriously difficult for AI to produce correctly \u2014 not because AI models lack [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"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":""},"categories":[1],"tags":[],"class_list":["post-4966","post","type-post","status-publish","format-standard","hentry","category-latest-news"],"jetpack_featured_media_url":"","jetpack_likes_enabled":true,"jetpack-related-posts":[],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=\/wp\/v2\/posts\/4966","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4966"}],"version-history":[{"count":0,"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=\/wp\/v2\/posts\/4966\/revisions"}],"wp:attachment":[{"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4966"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4966"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontlinenewsng.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}