{"id":122,"date":"2019-10-07T14:11:12","date_gmt":"2019-10-07T18:11:12","guid":{"rendered":"https:\/\/www.bates.edu\/webtech\/?p=122"},"modified":"2026-02-06T10:24:10","modified_gmt":"2026-02-06T15:24:10","slug":"switching-from-shortcodes-to-blocks-part-1","status":"publish","type":"post","link":"https:\/\/www.bates.edu\/webtech\/2019\/10\/07\/switching-from-shortcodes-to-blocks-part-1\/","title":{"rendered":"Switching from Shortcodes to Blocks, part 1"},"content":{"rendered":"\n<p>Lately we&#8217;ve been putting a lot of effort into increasing the adoption rate of the WordPress block editor on campus. One of the larger hurdles in that process is how many custom shortcodes we have in use. The vast majority of these should be switched over to a corresonding block. In order to do so, we can indicate in the block configuration exactly how that transformation should be made. Unfortunately, at the time of this writing, the <a href=\"https:\/\/developer.wordpress.org\/block-editor\/developers\/\">WordPress Developer Documentation<\/a> for Blocks is very spotty, and there&#8217;s one major gap in the current functionality. <\/p>\n\n\n\n<p>In this post I&#8217;d like to outline what I&#8217;ve learned as I wrote code allowing for the transformation of a custom shortcode to a block. I&#8217;m going to assume that you already have a basic understanding of how to write a block for the WordPress Block Editor. If not, you&#8217;ll probably want to work your way through some <a href=\"https:\/\/developer.wordpress.org\/block-editor\/tutorials\/block-tutorial\/writing-your-first-block-type\/\">WordPress developer tutorials<\/a> first. <\/p>\n\n\n\n<p>There are three scenarios we&#8217;ll go over: a shortcode with no inner content\/end tag, a shortcode with inner content which will be used as a block attribute, and a shortcode with inner content which will be used as inner blocks. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"no-inner-content\">Shortcodes with no inner content<\/h2>\n\n\n\n<p>If the shortcode you&#8217;re working with has no end tag (no inner content) and the target block has no <code>innerBlocks<\/code>,  the transformation is straightforward. This method is laid out relatively clearly in <a href=\"https:\/\/developer.wordpress.org\/block-editor\/developers\/block-api\/block-registration\/#transforms-optional\">the docs<\/a>, and I&#8217;ll briefly go over it here. Let&#8217;s use for this easy example the following shortcode: <\/p>\n\n\n\n<pre class=\"wp-block-code language-html language-plain\"><code>&#91;button link=\"https:\/\/example.com\" text=\"Click Here\"]<\/code><\/pre>\n\n\n\n<p>As part of your block registration, create a <strong>transforms<\/strong> key, with an object as the value. That object can have two keys <code>from<\/code> and <code>to<\/code> (only one is necessary). The value of those keys is an array of objects, with each object being a transformation configuration that might be used. <\/p>\n\n\n\n<p>We&#8217;ll add a configuration object to the from array with the <code>type: \"shortcode\"<\/code>. Here&#8217;s what it looks like so far:<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>registerBlockType('example\/button', {\n    ...\n    attributes: {\n        url: {\n            type: 'string',\n            ...\n        },\n        text: {\n            type: 'string',\n            ...\n        },\n    },\n    transforms: {\n       from: &#91;\n          {\n             type: \"shortcode\",\n          }\n       ],\n    },\n    ...\n}<\/code><\/pre>\n\n\n\n<p>Now, add the <code>tag<\/code> key with the value matching the shortcode. In our case, it&#8217;s the <strong>button<\/strong> shortcode.<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>...\ntype: \"shortcode\",\ntag: \"button\",\n...<\/code><\/pre>\n\n\n\n<p>Lastly, make an <code>attributes<\/code> key with an object; the object&#8217;s keys should match the block&#8217;s attributes. Each of <em>those<\/em> objects should have a key indicating the type, and a key named <strong>shortcode<\/strong>. The <strong>shortcode<\/strong> key should hold a function which will return the value of the block attribute. The first parameter of that function holds the shortcode attributes. If you want to transfer a shortcode&#8217;s attribute value directly to the block&#8217;s attribute value, it&#8217;s straightforward:<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>type: \"shortcode\",\ntag: \"button\",\nattributes: {\n   text: {\n      type: \"string\",\n      shortcode: function(attributes) {\n         return attributes.named.text;\n      },\n   url: {\n      type: \"string\",\n      shortcode: function( attributes ) {\n         return attributes.named.link;\n      },\n   },\n}<\/code><\/pre>\n\n\n\n<p>Or, if you prefer a newer code style:<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>type: \"shortcode\",\ntag: \"button\",\nattributes: {\n   text: {\n      type: \"string\",\n      shortcode: attributes =&gt; attributes.named.text,\n   url: {\n      type: \"string\",\n      shortcode: attributes =&gt; attributes.named.link,\n   },\n}<\/code><\/pre>\n\n\n\n<p>That&#8217;s it. When the page is transform from old editor to block editor, and the content is updated, the shortcode will be changed to the block with the attributes mapped as you&#8217;ve laid out. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">&#8220;named&#8221; vs. &#8220;numeric&#8221;<\/h4>\n\n\n\n<p>It&#8217;s worth briefly noting that WordPress handles attributes with values slightly differently from attributes that are just a keyword. Any attribute that has a value can be found in <code>attributes.named<\/code> object as a key=value pair. Any attribute which is just a keyword can be found in the <code>attributes.numeric<\/code> array. So for example, given the following shortcode:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html language-plain\"><code>&#91;button link=\"https:\/\/example.com\" newwindow]<\/code><\/pre>\n\n\n\n<p>the attributes parameter in the shortcode function would look like:<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>{\n   named: {\n      link: \"https:\/\/example.com\"\n   },\n   numeric: &#91;\n      \"newwindow\"\n   ],\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"content-as-attribute\">Using shortcode inner content in a block attribute<\/h2>\n\n\n\n<p>To move on to the second scenario, let&#8217;s change example code a little; we&#8217;ll use the same block, but the following shortcode:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html language-plain\"><code>&#91;button link=\"https:\/\/example.com\"]Click Here&#91;\/button]<\/code><\/pre>\n\n\n\n<p>In this shortcode, we have inner content, and we&#8217;ll want to use that for the <strong>text<\/strong> attribute in the block.  This method is not documented in the docs, but can be seen by trawling through the <a href=\"https:\/\/github.com\/wordpress\/gutenberg\">github repo<\/a>. To access the shortcode inner content we have to look at the second parameter of the <code>transforms.from.attributes[attribute].shortcode<\/code> function. So we&#8217;ll change:<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>text: {\n   type: \"string\",\n   shortcode: function(attributes) {\n      return attributes.named.text;\n   },\n}<\/code><\/pre>\n\n\n\n<p>to:<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>text: {\n   type: \"string\",\n   shortcode: function(attributes, data) {\n      return data.shortcode.content;\n   },<\/code><\/pre>\n\n\n\n<p>In an example I saw in the core code, <code>autop<\/code> and <code>removep<\/code> was used to clean up the formatting and prep the html for an attribute. To do that, you&#8217;ll need to first import those functions from the <code>wp.autop<\/code> library (this goes at the top of your code):<\/p>\n\n\n\n<pre class=\"wp-block-code language-js language-plain\"><code>const { autop, removep } = wp.autop;<\/code><\/pre>\n\n\n\n<p>and then change <code>return data.shortcode.content<\/code> to <code>return removep( autop( data.shortcode.content) )<\/code> If you do this, remember to add <code>wp-autop<\/code> as a dependency of your block&#8217;s javascript file when it&#8217;s enqueued. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Those are the first two scenarios, one which is documented well, and one not so much. <strong>Next up is part two: <a href=\"https:\/\/www.bates.edu\/webtech\/2019\/10\/07\/switching-from-shortcodes-to-blocks-part2\/\">Using shortcode inner content as innerBlocks<\/a><\/strong>. Did this help at all? Let me know in the comments section, and thanks for reading!  <\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button is-style-blue-button with-arrow large-button with-arrow large-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.bates.edu\/webtech\/2019\/10\/07\/switching-from-shortcodes-to-blocks-part2\/\">Read Part 2 <\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Lately we&#8217;ve been putting a lot of effort into increasing the adoption&hellip;<\/p>\n","protected":false},"author":428,"featured_media":157,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_hide_ai_chatbot":false,"_ai_chatbot_style":"","associated_faculty":[],"_Page_Specific_Css":"","_bates_restrict_mod":false,"_table_of_contents_display":false,"_table_of_contents_location":"","_table_of_contents_disableSticky":false,"_is_featured":false,"footnotes":"","_bates_seo_meta_description":"","_bates_seo_block_robots":false,"_bates_seo_sharing_image_id":0,"_bates_seo_sharing_image_twitter_id":0,"_bates_seo_share_title":"","_bates_seo_canonical_overwrite":"","_bates_seo_twitter_template":""},"categories":[1],"tags":[10,7,11],"class_list":["post-122","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-articles","tag-block-editor","tag-javascript","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/posts\/122","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/users\/428"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/comments?post=122"}],"version-history":[{"count":3,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/posts\/122\/revisions"}],"predecessor-version":[{"id":317,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/posts\/122\/revisions\/317"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/media\/157"}],"wp:attachment":[{"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/media?parent=122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/categories?post=122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bates.edu\/webtech\/wp-json\/wp\/v2\/tags?post=122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}