{"id":1365,"date":"2018-09-22T14:55:11","date_gmt":"2018-09-22T12:55:11","guid":{"rendered":"https:\/\/felix-arntz.me\/?p=1365"},"modified":"2025-02-11T08:12:23","modified_gmt":"2025-02-11T16:12:23","slug":"building-a-reusable-gutenberg-section-block","status":"publish","type":"post","link":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/","title":{"rendered":"Building a Reusable Gutenberg Section Block"},"content":{"rendered":"\n<p>As I announced in <a href=\"https:\/\/felix-arntz.me\/blog\/site-is-now-gutenberg-and-amp\/\">my last post in which I explained how I updated my website using Gutenberg and AMP<\/a>, I would like to share some more details on specific implementations for some of the block types and AMP support integrations. Let&#8217;s start today with looking into building a reusable Gutenberg section block type. What do I mean by this? It is common for websites to have their main content width limited to a maximum, to keep line lengths readable on larger screens. However, sometimes you still want certain components to break out of those limitations, or you might even want to break an entire page into different full-width sections which are differentiated by their visual appearance and allow to host content that itself is then again limited in its maximum width. The homepage of my website makes heavy use of this, if you prefer to see an example, or you can also look at the following example blocks embedded in this post. (Note that you will need a screen with a resolution of at least around 1200px width in order to see the width limits to take effect.)<\/p>\n\n\n\n<!--more-->\n\n\n\n<div class=\"lal-blocks-section is-style-alternate alignfull\"><div class=\"lal-blocks-section-inner is-site-width\">\n\n<p>This is a full-width section with alternate color, where the inner content width is limited to the maximum site content width (which is&nbsp;<code>72rem<\/code> on my website).<\/p>\n\n<\/div><\/div>\n\n\n\n<div class=\"lal-blocks-section is-style-highlight alignwide\"><div class=\"lal-blocks-section-inner is-content-width\">\n\n<p>This is a wide-width section with highlight color, where the inner content width is limited to the typical content width (I typically use <code>48rem<\/code>).<\/p>\n\n\n<p>Of course you can put anything you like into a section, not only text &#8211; so here you see an image of a great dinner and conversations at WCEU with XWP and Multisite friends:<\/p>\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"576\" src=\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/06\/dinner-xwp-multisite-768x576.jpg\" alt=\"Dinner with XWP and Multisite Friends\" class=\"wp-image-1139\" srcset=\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/06\/dinner-xwp-multisite-768x576.jpg 768w, https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/06\/dinner-xwp-multisite-300x225.jpg 300w, https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/06\/dinner-xwp-multisite.jpg 800w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/figure>\n\n<\/div><\/div>\n\n\n\n<div class=\"lal-blocks-section alignwide\"><div class=\"lal-blocks-section-inner is-content-width\">\n\n<p>Oh, and sections can also have background images, which you can see showcased on my homepage.<\/p>\n\n<\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">The Section Component<\/h2>\n\n\n\n<p>Instead of having the block type control everything, I initially wanted to ensure to keep the section markup controlled separately from the actual block type that allows controlling them. Therefore I introduced a <code>Section<\/code> React component that is reusable and that I could theoretically also use in other block types and even entirely separate use-cases on my website. The component is stateless and rather simple, so here is the code for it:<\/p>\n\n\n<pre class=\"alignwide\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> classnames <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'classnames'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'.\/style.scss'<\/span>;\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Section<\/span>(<span class=\"hljs-params\"> { colorScheme, contentMaxWidth, className, children, ...props } <\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">const<\/span> wrapperClasses = classnames(\n\t\t<span class=\"hljs-string\">'lal-blocks-section'<\/span>,\n\t\t{\n\t\t\t<span class=\"hljs-string\">'is-style-alternate'<\/span>: colorScheme === <span class=\"hljs-string\">'alternate'<\/span>,\n\t\t\t<span class=\"hljs-string\">'is-style-highlight'<\/span>: colorScheme === <span class=\"hljs-string\">'highlight'<\/span>,\n\t\t},\n\t\tclassName\n\t);\n\t<span class=\"hljs-keyword\">const<\/span> innerClasses = classnames(\n\t\t<span class=\"hljs-string\">'lal-blocks-section-inner'<\/span>,\n\t\t<span class=\"hljs-string\">`is-<span class=\"hljs-subst\">${ contentMaxWidth || <span class=\"hljs-string\">'site'<\/span> }<\/span>-width`<\/span>,\n\t);\n\t<span class=\"hljs-keyword\">return<\/span> (\n\t\t<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n\t\t\t<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">wrapperClasses<\/span> }\n\t\t\t{ <span class=\"hljs-attr\">...props<\/span> }\n\t\t&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n\t\t\t\t<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">innerClasses<\/span> }\n\t\t\t&gt;<\/span>\n\t\t\t\t{ children }\n\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\t);\n}\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Section;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I&#8217;ll explain the code a bit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The markup consists of an outer <code>div<\/code> that is used to control the width and general appearance of the section. In addition, there is an inner <code>div<\/code> to allow limiting the maximum width of the nested content. That <code>div<\/code> then actually hosts the content.<\/li>\n\n\n\n<li>There are two major options for the component that you can pass in as props:\n<ul class=\"wp-block-list\">\n<li>The color scheme, for which I personally support three values &#8220;default&#8221;, &#8220;alternate&#8221; and &#8220;highlight&#8221; (for &#8220;default&#8221;, no extra CSS class is added).<\/li>\n\n\n\n<li>The maximum content width, which accepts an identifier such as &#8220;site&#8221;, which is then turned into an &#8220;is-site-width&#8221; CSS class on the inner <code>div<\/code>. The CSS is then responsible for styling it.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>You can also pass in a custom class name for the section, which theoretically can be anything you like. Specifically for the block type, I will use this to pass in the <code>alignfull<\/code> or <code>alignwide<\/code> class (from default Gutenberg support) that will determine the width of the section itself.<\/li>\n\n\n\n<li>Of course any markup to be included within the section can be passed as well (via the <code>children<\/code> prop).<\/li>\n\n\n\n<li>Last but not least, any additional props that you pass in will be interpreted as extra attributes for the section&#8217;s outer <code>div<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>I include a&nbsp;<code>style.scss<\/code>&nbsp;file for the section component specifically, which Webpack will later extract out and generate a CSS file from it (look into&nbsp;<a href=\"https:\/\/github.com\/zgordon\/gutenberg-course\">Zac Gordon&#8217;s Gutenberg Course files<\/a>&nbsp;and also the great course itself, if you want to learn more about how this works). In the CSS, I then provide styling for the classes, such as handling foreground and background colors depending on the color scheme class and the maximum width based on the content maximum width class. Since these are very theme-specific, the plugin that contains the block types for my website allows to disable the default stylesheet via <code>add_theme_support( 'disable-felix-arntz-block-styles' )<\/code>, which I actually make use of. Of course I wouldn&#8217;t <em>have<\/em> to do that, because the plugin is only used on my site anyway. But since I like to keep things reusable, I try to write most code as such.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Section Block Type<\/h2>\n\n\n\n<p>Since we already looked at creating a component that takes care of the section markup, the block type is mostly responsible for the editor UI and the attributes available. The following attributes are needed:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>colorScheme<\/code> attribute is passed to the <code>Section<\/code> component as a prop directly. In order to control it in the editor, I use the&nbsp;<a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/master\/packages\/components\/src\/select-control\"><code>wp.components.SelectControl<\/code><\/a> component for a dropdown, making the three options &#8220;default&#8221;, &#8220;alternate&#8221;, and &#8220;highlight&#8221; available.<\/li>\n\n\n\n<li>The <code>contentMaxWidth<\/code> attribute is passed to the <code>Section<\/code> component as a prop directly as well. It uses another dropdown with the options &#8220;content&#8221;, &#8220;site&#8221; and &#8220;full&#8221;.<\/li>\n\n\n\n<li>Later during development, I decided to add support for background images too as mentioned before, so two attributes&nbsp;<code>attachmentId<\/code> and <code>attachmentUrl<\/code> take care of this. Using the <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/master\/packages\/editor\/src\/components\/media-upload\"><code>wp.editor.MediaUpload<\/code><\/a> component, an image can be set.<\/li>\n\n\n\n<li>You might wonder now how the width of the section itself is determined because there is no attribute for it. There is actually a super-cool integration in Gutenberg itself already that we can just use to make that happen: In the block type settings, you need to simply declare <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/blob\/master\/packages\/editor\/src\/hooks\/align.js\">block type alignment<\/a> support via <code>{ supports: { align: true } }<\/code>. This will make the controls to set alignment appear automatically, and the respective CSS class will be added to the block-generated markup&#8217;s outer element. One extra detail: For the specific case of the section, I only wanted to allow wide alignment and full alignment, as left-\/center-\/right-aligned sections wouldn&#8217;t really make sense for my specific implementation. I accomplished this by changing the alignment support entry to <code>align: [ 'wide', 'full' ]<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>That&#8217;s pretty much it for the attributes and controls already. Before I explain the generated markup and further tweaks, I&#8217;d like you to get familiar with the entire block type first by looking at its code:<\/p>\n\n\n<pre class=\"alignwide\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> classnames <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'classnames'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> BlockType <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/block-type'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Section <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/..\/components\/section'<\/span>;\n<span class=\"hljs-keyword\">const<\/span> { Fragment } = wp.element;\n<span class=\"hljs-keyword\">const<\/span> {\n\tPanelBody,\n\tSelectControl,\n\tBaseControl,\n\tIconButton,\n} = wp.components;\n<span class=\"hljs-keyword\">const<\/span> {\n\tInspectorControls,\n\tMediaUpload,\n\tInnerBlocks,\n} = wp.editor;\n<span class=\"hljs-keyword\">const<\/span> { __, _x } = wp.i18n;\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> name = <span class=\"hljs-string\">'felix-arntz\/section'<\/span>;\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> settings = {\n\t<span class=\"hljs-attr\">title<\/span>: __( <span class=\"hljs-string\">'Section'<\/span>, <span class=\"hljs-string\">'felix-arntz-blocks'<\/span> ),\n\t<span class=\"hljs-attr\">description<\/span>: __( <span class=\"hljs-string\">'Add a section that separates content, and put any other block into it.'<\/span>, <span class=\"hljs-string\">'felix-arntz-blocks'<\/span> ),\n\t<span class=\"hljs-attr\">category<\/span>: <span class=\"hljs-string\">'layout'<\/span>,\n\t<span class=\"hljs-attr\">icon<\/span>: <span class=\"hljs-string\">'welcome-widgets-menus'<\/span>,\n\t<span class=\"hljs-attr\">keywords<\/span>: &#91;\n\t\t_x( <span class=\"hljs-string\">'section'<\/span>, <span class=\"hljs-string\">'keyword'<\/span>, <span class=\"hljs-string\">'felix-arntz-blocks'<\/span> ),\n\t\t_x( <span class=\"hljs-string\">'separator'<\/span>, <span class=\"hljs-string\">'keyword'<\/span>, <span class=\"hljs-string\">'felix-arntz-blocks'<\/span> ),\n\t],\n\t<span class=\"hljs-attr\">supports<\/span>: {\n\t\t<span class=\"hljs-attr\">align<\/span>: &#91; <span class=\"hljs-string\">'wide'<\/span>, <span class=\"hljs-string\">'full'<\/span> ],\n\t\t<span class=\"hljs-attr\">anchor<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\t},\n\t<span class=\"hljs-attr\">attributes<\/span>: {\n\t\t<span class=\"hljs-attr\">colorScheme<\/span>: {\n\t\t\t<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'string'<\/span>,\n\t\t\t<span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-string\">'default'<\/span>,\n\t\t},\n\t\t<span class=\"hljs-attr\">contentMaxWidth<\/span>: {\n\t\t\t<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'string'<\/span>,\n\t\t\t<span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-string\">'site'<\/span>,\n\t\t},\n\t\t<span class=\"hljs-attr\">attachmentId<\/span>: {\n\t\t\t<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'number'<\/span>,\n\t\t},\n\t\t<span class=\"hljs-attr\">attachmentUrl<\/span>: {\n\t\t\t<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'string'<\/span>,\n\t\t},\n\t},\n\t<span class=\"hljs-attr\">edit<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">props<\/span> =&gt;<\/span> {\n\t\t<span class=\"hljs-keyword\">const<\/span> { attributes, setAttributes } = props;\n\t\t<span class=\"hljs-keyword\">const<\/span> { colorScheme, contentMaxWidth, attachmentId, attachmentUrl } = attributes;\n\t\t<span class=\"hljs-keyword\">const<\/span> onSelectImage = <span class=\"hljs-function\"><span class=\"hljs-params\">media<\/span> =&gt;<\/span> {\n\t\t\t<span class=\"hljs-keyword\">if<\/span> ( ! media || ! media.id || ! media.url ) {\n\t\t\t\tsetAttributes( { <span class=\"hljs-attr\">attachmentId<\/span>: <span class=\"hljs-literal\">undefined<\/span>, <span class=\"hljs-attr\">attachmentUrl<\/span>: <span class=\"hljs-literal\">undefined<\/span> } );\n\t\t\t\t<span class=\"hljs-keyword\">return<\/span>;\n\t\t\t}\n\t\t\tsetAttributes( { <span class=\"hljs-attr\">attachmentId<\/span>: media.id, <span class=\"hljs-attr\">attachmentUrl<\/span>: media.url } );\n\t\t};\n\t\t<span class=\"hljs-keyword\">return<\/span> (\n\t\t\t<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Fragment<\/span>&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InspectorControls<\/span>&gt;<\/span>\n\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PanelBody<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">__<\/span>( '<span class=\"hljs-attr\">Section<\/span> <span class=\"hljs-attr\">Settings<\/span>', '<span class=\"hljs-attr\">felix-arntz-blocks<\/span>' ) }&gt;<\/span>\n\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SelectControl<\/span>\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">label<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">__<\/span>( '<span class=\"hljs-attr\">Color<\/span> <span class=\"hljs-attr\">Scheme<\/span>', '<span class=\"hljs-attr\">felix-arntz-blocks<\/span>' ) }\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">colorScheme<\/span> || '<span class=\"hljs-attr\">default<\/span>' }\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">value<\/span> =&gt;<\/span> setAttributes( { colorScheme: ( 'default' !== value ) ? value : undefined } ) }\n\t\t\t\t\t\t\toptions={ &#91;\n\t\t\t\t\t\t\t\t{ value: 'default', label: __( 'Default', 'felix-arntz-blocks' ) },\n\t\t\t\t\t\t\t\t{ value: 'alternate', label: __( 'Alternate', 'felix-arntz-blocks' ) },\n\t\t\t\t\t\t\t\t{ value: 'highlight', label: __( 'Highlight', 'felix-arntz-blocks' ) },\n\t\t\t\t\t\t\t] }\n\t\t\t\t\t\t\/&gt;\n\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SelectControl<\/span>\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">label<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">__<\/span>( '<span class=\"hljs-attr\">Maximum<\/span> <span class=\"hljs-attr\">Content<\/span> <span class=\"hljs-attr\">Width<\/span>', '<span class=\"hljs-attr\">felix-arntz-blocks<\/span>' ) }\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">contentMaxWidth<\/span> || '<span class=\"hljs-attr\">site<\/span>' }\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">value<\/span> =&gt;<\/span> setAttributes( { contentMaxWidth: ( 'site' !== value ) ? value : undefined } ) }\n\t\t\t\t\t\t\toptions={ &#91;\n\t\t\t\t\t\t\t\t{ value: 'content', label: __( 'Content Width', 'felix-arntz-blocks' ) },\n\t\t\t\t\t\t\t\t{ value: 'site', label: __( 'Site Width', 'felix-arntz-blocks' ) },\n\t\t\t\t\t\t\t\t{ value: 'full', label: __( 'Full Width', 'felix-arntz-blocks' ) },\n\t\t\t\t\t\t\t] }\n\t\t\t\t\t\t\/&gt;\n\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">BaseControl<\/span>\n\t\t\t\t\t\t\t<span class=\"hljs-attr\">label<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">__<\/span>( '<span class=\"hljs-attr\">Background<\/span> <span class=\"hljs-attr\">Image<\/span>', '<span class=\"hljs-attr\">felix-arntz-blocks<\/span>' ) }\n\t\t\t\t\t\t&gt;<\/span>\n\t\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MediaUpload<\/span>\n\t\t\t\t\t\t\t\t<span class=\"hljs-attr\">onSelect<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">onSelectImage<\/span> }\n\t\t\t\t\t\t\t\t<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"image\"<\/span>\n\t\t\t\t\t\t\t\t<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">attachmentId<\/span> }\n\t\t\t\t\t\t\t\t<span class=\"hljs-attr\">render<\/span>=<span class=\"hljs-string\">{<\/span> ( { <span class=\"hljs-attr\">open<\/span> } ) =&gt;<\/span> (\n\t\t\t\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">IconButton<\/span>\n\t\t\t\t\t\t\t\t\t\t<span class=\"hljs-attr\">icon<\/span>=<span class=\"hljs-string\">\"admin-media\"<\/span>\n\t\t\t\t\t\t\t\t\t\t<span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">open<\/span> }\n\t\t\t\t\t\t\t\t\t&gt;<\/span>\n\t\t\t\t\t\t\t\t\t\t{ attachmentId ? __( 'Edit Image', 'felix-arntz-blocks' ) : __( 'Add Image', 'felix-arntz-blocks' ) }\n\t\t\t\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">IconButton<\/span>&gt;<\/span>\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\/&gt;\n\t\t\t\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">BaseControl<\/span>&gt;<\/span>\n\t\t\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">PanelBody<\/span>&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">InspectorControls<\/span>&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Section<\/span>\n\t\t\t\t\t<span class=\"hljs-attr\">colorScheme<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">colorScheme<\/span> }\n\t\t\t\t\t<span class=\"hljs-attr\">contentMaxWidth<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">contentMaxWidth<\/span> }\n\t\t\t\t\t<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">classnames<\/span>(\n\t\t\t\t\t\t<span class=\"hljs-attr\">attachmentId<\/span> &amp;&amp; `<span class=\"hljs-attr\">has-background-image-<\/span>${ <span class=\"hljs-attr\">attachmentId<\/span> }`\n\t\t\t\t\t) }\n\t\t\t\t\t<span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">attachmentUrl<\/span> ? { <span class=\"hljs-attr\">backgroundImage:<\/span> `<span class=\"hljs-attr\">url<\/span>('${ <span class=\"hljs-attr\">attachmentUrl<\/span> }')` } <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">undefined<\/span> }\n\t\t\t\t&gt;<\/span>\n\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InnerBlocks<\/span> \/&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Section<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Fragment<\/span>&gt;<\/span><\/span>\n\t\t);\n\t},\n\t<span class=\"hljs-attr\">save<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">props<\/span> =&gt;<\/span> {\n\t\t<span class=\"hljs-keyword\">const<\/span> { attributes } = props;\n\t\t<span class=\"hljs-keyword\">const<\/span> { colorScheme, contentMaxWidth, attachmentId } = attributes;\n\t\t<span class=\"hljs-keyword\">return<\/span> (\n\t\t\t<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Section<\/span>\n\t\t\t\t<span class=\"hljs-attr\">colorScheme<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">colorScheme<\/span> }\n\t\t\t\t<span class=\"hljs-attr\">contentMaxWidth<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">contentMaxWidth<\/span> }\n\t\t\t\t<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">classnames<\/span>(\n\t\t\t\t\t<span class=\"hljs-attr\">attachmentId<\/span> &amp;&amp; `<span class=\"hljs-attr\">has-background-image-<\/span>${ <span class=\"hljs-attr\">attachmentId<\/span> }`\n\t\t\t\t) }\n\t\t\t&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InnerBlocks.Content<\/span> \/&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Section<\/span>&gt;<\/span><\/span>\n\t\t);\n\t},\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>As you can see, I used the <code>Section<\/code> component to render the block markup. You can already see the benefit of its reusability in action here: If we didn&#8217;t have the <code>Section<\/code> component, but had to manually generate the HTML markup, we would have to write a lot of similar code twice because the markup is generated both in the Gutenberg editor (via the <code>edit()<\/code> function) and for the actual post content (via the <code>save()<\/code> function).<\/p>\n\n\n\n<p>To allow putting other block types inside of a section, I used the <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/master\/packages\/editor\/src\/components\/inner-blocks\"><code>wp.editor.InnerBlocks<\/code><\/a> component. Since I didn&#8217;t want to enforce any restrictions on which block types could be nested within a section, I simply use the component without passing any props.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Responsive Section Background Image<\/h3>\n\n\n\n<p>For the most important functionality of the block type, I think I&#8217;ve already explained everything important. Let&#8217;s now look at <strong>one major tweak<\/strong> I included, which affects how the background image is included. I could include it simply by passing a <code>style<\/code> prop to the <code>Section<\/code> component (which, if you remember, would then be interpreted as a <code>style<\/code> attribute on the section&#8217;s outer <code>div<\/code>). However, that would mean that the one image would be used as background regardless of the screen size. Since you most likely want the section to look good, you would choose an image size that works well with large screens, which would then mean lots of unnecessary overhead for smaller devices, and it would significantly decrease performance on phones with a 3G internet connection, just as an example. I wanted to find a way around that, to ensure a fitting image size is served based on the device.<\/p>\n\n\n\n<p>As you can see, for the Gutenberg editor, I <em>do<\/em> include the attachment URL directly via <code>style<\/code>, since I wanted to keep things simple there and performance there is not as critical as in the frontend. However, when saving the post content, I add a CSS class <code>has-background-image-${ attachmentId }<\/code>&nbsp;instead, which contains the attachment ID. Without any further tweaks of course this would render without the background image showing up. Let&#8217;s look at how I used a server-side tweak to make it work.<\/p>\n\n\n\n<p>By adding a filter on <code>the_content<\/code>, it is possible to check for occurrences of the above CSS class in the content. By matching them and extracting the attachment ID, we can dynamically generate CSS rules for each class, supporting media queries based on the image sizes that the respective attachment has. All rules generated are then eventually appended to the content in a <code>style<\/code> tag. While I could have theoretically done this in the block type code itself in JavaScript, this would have hardcoded the rather complex&nbsp;<code>style<\/code> tag for each block. Since the available image sizes might change and since a <code>style<\/code> tag shouldn&#8217;t preferably be part of a Gutenberg block type, I decided to use this more dynamic, server-side approach. Here you can see what my code for parsing the classes and generating a <code>style<\/code> tag from it looks like:<\/p>\n\n\n<pre class=\"alignwide\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/**\n * Adds background image styles to any content elements using a `has-background-image-{$id}` class.\n *\n * This function uses all available image sizes to generate media queries, including taking\n * care of retina displays.\n *\n * <span class=\"hljs-doctag\">@since<\/span> 1.0.0\n *\n * <span class=\"hljs-doctag\">@param<\/span> string $content Post content.\n * <span class=\"hljs-doctag\">@return<\/span> string Filtered post content.\n *\/<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">felix_arntz_blocks_enhance_background_image_style<\/span><span class=\"hljs-params\">( $content )<\/span> <\/span>{\n\t<span class=\"hljs-keyword\">if<\/span> ( ! preg_match_all( <span class=\"hljs-string\">'\/has-background-image-(\\d+)\/'<\/span>, $content, $matches, PREG_PATTERN_ORDER ) ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> $content;\n\t}\n\t$styles = <span class=\"hljs-keyword\">array<\/span>();\n\t$attachment_ids = array_unique( array_map( <span class=\"hljs-string\">'absint'<\/span>, $matches&#91;<span class=\"hljs-number\">1<\/span>] ) );\n\t<span class=\"hljs-keyword\">foreach<\/span> ( $attachment_ids <span class=\"hljs-keyword\">as<\/span> $attachment_id ) {\n\t\t$meta = wp_get_attachment_metadata( $attachment_id );\n\t\t<span class=\"hljs-keyword\">if<\/span> ( ! $meta ) {\n\t\t\t<span class=\"hljs-keyword\">continue<\/span>;\n\t\t}\n\t\t$attachment_url = wp_get_attachment_url( $attachment_id );\n\t\t$base_url       = str_replace( wp_basename( $attachment_url ), <span class=\"hljs-string\">''<\/span>, $attachment_url );\n\t\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">empty<\/span>( $meta&#91;<span class=\"hljs-string\">'sizes'<\/span>] ) ) {\n\t\t\t$styles&#91;] = <span class=\"hljs-string\">\"\n\t.has-background-image-{$attachment_id} {\n\t\tbackground-image: url('{$attachment_url}');\n\t}\"<\/span>;\n\t\t\t<span class=\"hljs-keyword\">continue<\/span>;\n\t\t}\n\t\t$sizes = wp_list_sort( $meta&#91;<span class=\"hljs-string\">'sizes'<\/span>], <span class=\"hljs-string\">'width'<\/span>, <span class=\"hljs-string\">'ASC'<\/span>, <span class=\"hljs-keyword\">true<\/span> );\n\t\t<span class=\"hljs-keyword\">if<\/span> ( ! <span class=\"hljs-keyword\">isset<\/span>( $sizes&#91;<span class=\"hljs-string\">'full'<\/span>] ) ) {\n\t\t\t$sizes&#91;<span class=\"hljs-string\">'full'<\/span>] = <span class=\"hljs-keyword\">array<\/span>( <span class=\"hljs-string\">'url'<\/span> =&gt; $attachment_url, <span class=\"hljs-string\">'width'<\/span> =&gt; $meta&#91;<span class=\"hljs-string\">'width'<\/span>] );\n\t\t}\n\t\t$sizes = array_values( $sizes );\n\t\t$style            = <span class=\"hljs-keyword\">array<\/span>();\n\t\t$widths           = <span class=\"hljs-keyword\">array<\/span>();\n\t\t$min_width        = <span class=\"hljs-number\">0<\/span>;\n\t\t$min_width_retina = <span class=\"hljs-number\">0<\/span>;\n\t\t$size_count       = count( $sizes );\n\t\t<span class=\"hljs-keyword\">foreach<\/span> ( $sizes <span class=\"hljs-keyword\">as<\/span> $index =&gt; $size_meta ) {\n\t\t\t<span class=\"hljs-keyword\">if<\/span> ( $size_meta&#91;<span class=\"hljs-string\">'width'<\/span>] &lt; <span class=\"hljs-number\">480<\/span> || in_array( $size_meta&#91;<span class=\"hljs-string\">'width'<\/span>], $widths, <span class=\"hljs-keyword\">true<\/span> ) ) {\n\t\t\t\t<span class=\"hljs-keyword\">continue<\/span>;\n\t\t\t}\n\t\t\t$widths&#91;] = $size_meta&#91;<span class=\"hljs-string\">'width'<\/span>];\n\t\t\t<span class=\"hljs-keyword\">if<\/span> ( $index === $size_count - <span class=\"hljs-number\">1<\/span> ) {\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Do not specify max-width for the largest available width.<\/span>\n\t\t\t\t$max_width        = <span class=\"hljs-number\">0<\/span>;\n\t\t\t\t$max_width_retina = <span class=\"hljs-number\">0<\/span>;\n\t\t\t} <span class=\"hljs-keyword\">else<\/span> {\n\t\t\t\t$max_width        = $size_meta&#91;<span class=\"hljs-string\">'width'<\/span>];\n\t\t\t\t$max_width_retina = $size_meta&#91;<span class=\"hljs-string\">'width'<\/span>] \/ <span class=\"hljs-number\">2<\/span>;\n\t\t\t}\n\t\t\t$media_query        = felix_arntz_blocks_get_media_query( $min_width, $max_width );\n\t\t\t$media_query_retina = felix_arntz_blocks_get_media_query_retina( $min_width_retina, $max_width_retina );\n\t\t\t$size_url = ! <span class=\"hljs-keyword\">empty<\/span>( $size_meta&#91;<span class=\"hljs-string\">'url'<\/span>] ) ? $size_meta&#91;<span class=\"hljs-string\">'url'<\/span>] : $base_url . $size_meta&#91;<span class=\"hljs-string\">'file'<\/span>];\n\t\t\t$style&#91;] = <span class=\"hljs-string\">\"\n\t@media {$media_query} {\n\t\t.has-background-image-{$attachment_id} {\n\t\t\tbackground-image: url('{$size_url}');\n\t\t}\n\t}\n\t@media {$media_query_retina} {\n\t\t.has-background-image-{$attachment_id} {\n\t\t\tbackground-image: url('{$size_url}');\n\t\t}\n\t}\"<\/span>;\n\t\t\t$min_width        = $max_width + <span class=\"hljs-number\">1<\/span>;\n\t\t\t$min_width_retina = $max_width_retina + <span class=\"hljs-number\">1<\/span>;\n\t\t}\n\t\t$styles&#91;] = implode( <span class=\"hljs-string\">''<\/span>, $style );\n\t}\n\t$content = <span class=\"hljs-string\">'&lt;style type=\"text\/css\"&gt;'<\/span> . implode( <span class=\"hljs-string\">''<\/span>, $styles ) . <span class=\"hljs-string\">'&lt;\/style&gt;'<\/span> . $content;\n\t<span class=\"hljs-keyword\">return<\/span> $content;\n}\nadd_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-keyword\">__NAMESPACE__<\/span> . <span class=\"hljs-string\">'\\\\felix_arntz_blocks_enhance_background_image_style'<\/span>, <span class=\"hljs-number\">100<\/span> );\n<span class=\"hljs-comment\">\/**\n * Gets a CSS media query string for the given minimum and maximum width.\n *\n * <span class=\"hljs-doctag\">@since<\/span> 1.0.0\n *\n * <span class=\"hljs-doctag\">@param<\/span> int $min_width Minimum width. If 0, it will not be considered.\n * <span class=\"hljs-doctag\">@param<\/span> int $max_width Maximum width. If 0, it will not be considered.\n * <span class=\"hljs-doctag\">@return<\/span> string Media query string.\n *\/<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">felix_arntz_blocks_get_media_query<\/span><span class=\"hljs-params\">( $min_width, $max_width )<\/span> <\/span>{\n\t<span class=\"hljs-keyword\">if<\/span> ( $min_width &amp;&amp; $max_width ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"screen and (min-width: {$min_width}px) and (max-width: {$max_width}px)\"<\/span>;\n\t}\n\t<span class=\"hljs-keyword\">if<\/span> ( $min_width ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"screen and (min-width: {$min_width}px)\"<\/span>;\n\t}\n\t<span class=\"hljs-keyword\">if<\/span> ( $max_width ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"screen and (max-width: {$max_width}px)\"<\/span>;\n\t}\n\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">''<\/span>;\n}\n<span class=\"hljs-comment\">\/**\n * Gets a CSS media query string for retina displays for the given minimum and maximum width.\n *\n * <span class=\"hljs-doctag\">@since<\/span> 1.0.0\n *\n * <span class=\"hljs-doctag\">@param<\/span> int $min_width Minimum width. If 0, it will not be considered.\n * <span class=\"hljs-doctag\">@param<\/span> int $max_width Maximum width. If 0, it will not be considered.\n * <span class=\"hljs-doctag\">@return<\/span> string Media query string for retina displays.\n *\/<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">felix_arntz_blocks_get_media_query_retina<\/span><span class=\"hljs-params\">( $min_width, $max_width )<\/span> <\/span>{\n\t<span class=\"hljs-keyword\">if<\/span> ( $min_width &amp;&amp; $max_width ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: {$min_width}px) and (max-width: {$max_width}px),\n\t\tscreen and (min-resolution: 192dpi) and (min-width: {$min_width}px) and (max-width: {$max_width}px),\n\t\tscreen and (min-resolution: 2dppx) and (min-width: {$min_width}px) and (max-width: {$max_width}px)\"<\/span>;\n\t}\n\t<span class=\"hljs-keyword\">if<\/span> ( $min_width ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: {$min_width}px),\n\t\tscreen and (min-resolution: 192dpi) and (min-width: {$min_width}px),\n\t\tscreen and (min-resolution: 2dppx) and (min-width: {$min_width}px)\"<\/span>;\n\t}\n\t<span class=\"hljs-keyword\">if<\/span> ( $max_width ) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: {$max_width}px),\n\t\tscreen and (min-resolution: 192dpi) and (max-width: {$max_width}px),\n\t\tscreen and (min-resolution: 2dppx) and (max-width: {$max_width}px)\"<\/span>;\n\t}\n\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">''<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For each attachment found, the code get its available image sizes and includes media query-based rules for them, unless the respective size is smaller than 480 pixels, which I decided to use as the low boundary for media queries that make sense (you probably wouldn&#8217;t wanna have media queries such as <code>screen and (min-width: 151px) and (max-width: 250px)<\/code> because no common screen width would benefit from them). In addition to the regular media queries, I also include media queries for retina displays, using the image size divided by 2 as media query boundary.<\/p>\n\n\n\n<p>As you can see in the code, it will take care of every occurrence of the&nbsp;<code>has-background-image-${ attachmentId }<\/code> class in the content, regardless of where it is used. This allows me to easily reuse this pattern in any other block type as well, and it also ensures the generated <code>style<\/code> tag works for all blocks in the content that have background images attached via that approach.<\/p>\n\n\n\n<p>Since the code is a bit complex, take some time following it through. There might be things you&#8217;re wondering about, so please let me know if I should explain a bit more.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>I hope this post gave you some inspiration for building custom Gutenberg block types and for approaching some of the challenges it might confront you with. I chose the section block type for this post because I think it is something typical that many websites could benefit from.<\/p>\n\n\n\n<p>Please let me know if you have questions regarding the implementation that didn&#8217;t become obvious for you after reading the post. I&#8217;d also be happy to see your implementations of a section block &#8211; by sharing our approaches, we&#8217;ll be able to combine them to use the best parts from all of them!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As I announced in my last post in which I explained how I updated my website using Gutenberg and AMP, I would like to share some more details on specific implementations for some of the block types and AMP support integrations. Let&#8217;s start today with looking into building a reusable Gutenberg section block type. What [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1373,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"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":[92,71,97],"tags":[],"class_list":["post-1365","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blocks","category-tutorials","category-wordpress"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.9 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Building a Reusable Gutenberg Section Block - felix-arntz.me<\/title>\n<meta name=\"description\" content=\"In this post I will explain how you can build a reusable Gutenberg block for a flexible-width section, including background image support.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building a Reusable Gutenberg Section Block - felix-arntz.me\" \/>\n<meta property=\"og:description\" content=\"In this post I will explain how you can build a reusable Gutenberg block for a flexible-width section, including background image support.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\" \/>\n<meta property=\"og:site_name\" content=\"felix-arntz.me\" \/>\n<meta property=\"article:published_time\" content=\"2018-09-22T12:55:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-11T16:12:23+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"560\" \/>\n\t<meta property=\"og:image:height\" content=\"315\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Felix\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@felixarntz\" \/>\n<meta name=\"twitter:site\" content=\"@felixarntz\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Felix\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\"},\"author\":{\"name\":\"Felix\",\"@id\":\"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55\"},\"headline\":\"Building a Reusable Gutenberg Section Block\",\"datePublished\":\"2018-09-22T12:55:11+00:00\",\"dateModified\":\"2025-02-11T16:12:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\"},\"wordCount\":1788,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55\"},\"image\":{\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg\",\"articleSection\":[\"Blocks\",\"Tutorials\",\"WordPress\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\",\"url\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\",\"name\":\"Building a Reusable Gutenberg Section Block - felix-arntz.me\",\"isPartOf\":{\"@id\":\"https:\/\/felix-arntz.me\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg\",\"datePublished\":\"2018-09-22T12:55:11+00:00\",\"dateModified\":\"2025-02-11T16:12:23+00:00\",\"description\":\"In this post I will explain how you can build a reusable Gutenberg block for a flexible-width section, including background image support.\",\"breadcrumb\":{\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage\",\"url\":\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg\",\"contentUrl\":\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg\",\"width\":560,\"height\":315,\"caption\":\"Building a Reusable Gutenberg Block\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/felix-arntz.me\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorials\",\"item\":\"https:\/\/felix-arntz.me\/blog\/category\/tutorials\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Building a Reusable Gutenberg Section Block\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/felix-arntz.me\/#website\",\"url\":\"https:\/\/felix-arntz.me\/\",\"name\":\"felix-arntz.me\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/felix-arntz.me\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55\",\"name\":\"Felix\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/felix-arntz.me\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/felix-arntz-site-icon.png\",\"contentUrl\":\"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/felix-arntz-site-icon.png\",\"width\":512,\"height\":512,\"caption\":\"Felix\"},\"logo\":{\"@id\":\"https:\/\/felix-arntz.me\/#\/schema\/person\/image\/\"},\"description\":\"Developer Programs Engineer at Google. WordPress Core Committer. Previously Yoast. Runner, musician, movie geek. Aprendiendo espa\u00f1ol. Fueled by Mountain Dew.\",\"sameAs\":[\"https:\/\/x.com\/felixarntz\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Building a Reusable Gutenberg Section Block - felix-arntz.me","description":"In this post I will explain how you can build a reusable Gutenberg block for a flexible-width section, including background image support.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/","og_locale":"en_US","og_type":"article","og_title":"Building a Reusable Gutenberg Section Block - felix-arntz.me","og_description":"In this post I will explain how you can build a reusable Gutenberg block for a flexible-width section, including background image support.","og_url":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/","og_site_name":"felix-arntz.me","article_published_time":"2018-09-22T12:55:11+00:00","article_modified_time":"2025-02-11T16:12:23+00:00","og_image":[{"width":560,"height":315,"url":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg","type":"image\/jpeg"}],"author":"Felix","twitter_card":"summary_large_image","twitter_creator":"@felixarntz","twitter_site":"@felixarntz","twitter_misc":{"Written by":"Felix","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#article","isPartOf":{"@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/"},"author":{"name":"Felix","@id":"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55"},"headline":"Building a Reusable Gutenberg Section Block","datePublished":"2018-09-22T12:55:11+00:00","dateModified":"2025-02-11T16:12:23+00:00","mainEntityOfPage":{"@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/"},"wordCount":1788,"commentCount":0,"publisher":{"@id":"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55"},"image":{"@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage"},"thumbnailUrl":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg","articleSection":["Blocks","Tutorials","WordPress"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/","url":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/","name":"Building a Reusable Gutenberg Section Block - felix-arntz.me","isPartOf":{"@id":"https:\/\/felix-arntz.me\/#website"},"primaryImageOfPage":{"@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage"},"image":{"@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage"},"thumbnailUrl":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg","datePublished":"2018-09-22T12:55:11+00:00","dateModified":"2025-02-11T16:12:23+00:00","description":"In this post I will explain how you can build a reusable Gutenberg block for a flexible-width section, including background image support.","breadcrumb":{"@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#primaryimage","url":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg","contentUrl":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg","width":560,"height":315,"caption":"Building a Reusable Gutenberg Block"},{"@type":"BreadcrumbList","@id":"https:\/\/felix-arntz.me\/blog\/building-a-reusable-gutenberg-section-block\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/felix-arntz.me\/"},{"@type":"ListItem","position":2,"name":"Tutorials","item":"https:\/\/felix-arntz.me\/blog\/category\/tutorials\/"},{"@type":"ListItem","position":3,"name":"Building a Reusable Gutenberg Section Block"}]},{"@type":"WebSite","@id":"https:\/\/felix-arntz.me\/#website","url":"https:\/\/felix-arntz.me\/","name":"felix-arntz.me","description":"","publisher":{"@id":"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/felix-arntz.me\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/felix-arntz.me\/#\/schema\/person\/c7c3c658d2e59bbddf3e8684a6846e55","name":"Felix","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/felix-arntz.me\/#\/schema\/person\/image\/","url":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/felix-arntz-site-icon.png","contentUrl":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/felix-arntz-site-icon.png","width":512,"height":512,"caption":"Felix"},"logo":{"@id":"https:\/\/felix-arntz.me\/#\/schema\/person\/image\/"},"description":"Developer Programs Engineer at Google. WordPress Core Committer. Previously Yoast. Runner, musician, movie geek. Aprendiendo espa\u00f1ol. Fueled by Mountain Dew.","sameAs":["https:\/\/x.com\/felixarntz"]}]}},"jetpack_featured_media_url":"https:\/\/felix-arntz.me\/wp-content\/uploads\/2018\/09\/building-a-reusable-gutenberg-block.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/posts\/1365","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/comments?post=1365"}],"version-history":[{"count":3,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/posts\/1365\/revisions"}],"predecessor-version":[{"id":1814,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/posts\/1365\/revisions\/1814"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/media\/1373"}],"wp:attachment":[{"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/media?parent=1365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/categories?post=1365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/felix-arntz.me\/api\/wp\/v2\/tags?post=1365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}