{"id":110,"date":"2024-10-03T12:47:11","date_gmt":"2024-10-03T16:47:11","guid":{"rendered":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/?page_id=110"},"modified":"2025-02-20T13:50:49","modified_gmt":"2025-02-20T18:50:49","slug":"inner-blocks","status":"publish","type":"page","link":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/handbook\/wp-core\/inner-blocks\/","title":{"rendered":"Inner Blocks"},"content":{"rendered":"\n<p><a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/wp\/5.8\/packages\/block-editor\/src\/components\/inner-blocks\" target=\"_blank\" rel=\"noreferrer noopener\">Inner blocks<\/a>, or nested blocks, is a component from the <code>@wordpress\/block-editor<\/code> package that allows a parent block to use other child blocks for its structure. These child blocks in the Inner Blocks component can be free form, allowing the child blocks to be rearranged, create multiples of, or locked into a specific pattern.<\/p>\n\n\n\n<p>There is more control over how inner blocks are presented, vs. block patterns, which are basically a suggested template that can be modified. Inner blocks can be locked into a specific pattern, or allow modification, but only with a restricted list of blocks.<\/p>\n\n\n\n<p>A few resources:<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/how-to-guides\/block-tutorial\/nested-blocks-inner-blocks\/\">WordPress Developer Resources: Nested Blocks<\/a><\/li><li><a href=\"https:\/\/www.briancoords.com\/build-custom-blocks-using-innerblocks\/\">Brian Coords: Build Custom Blocks Using InnerBlocks<\/a> <br>The video has a great explainer and example<\/li><li><a href=\"https:\/\/baker.dev\/articles\/using-wordpress-gutenbergs-innerblocks-component\/\">baker.dev: InnerBlocks: Gutenberg\u2019s Secret Weapon<\/a><\/li><\/ul>\n\n\n\n<p>To get started, create a new block by running<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm run new-id-block<\/code><\/pre>\n\n\n\n<p>Within the new block folder, <code>edit.js<\/code> and <code>save.js<\/code> are the two folders to work with. Below is <a href=\"https:\/\/github.com\/bacoords\/example-inner-block\/tree\/main\/src\">sample code<\/a> from Brian Coords:<\/p>\n\n\n\n<p>The two sections for <code>edit.js<\/code> are the import call and the edit function:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"562\" src=\"\/gutenberg\/files\/2025\/01\/SCR-20250123-pdhd-1024x562.png\" alt=\"Code block from edit.js showing the import and edit sections\" class=\"wp-image-470\" srcset=\"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-pdhd-1024x562.png 1024w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-pdhd-636x349.png 636w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-pdhd-768x421.png 768w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-pdhd-1536x843.png 1536w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-pdhd-2048x1124.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Within the <code>Edit<\/code> function, there are a few arguments\/settings:<\/p>\n\n\n\n<ul><li><code><a href=\"https:\/\/developer.wordpress.org\/block-editor\/how-to-guides\/block-tutorial\/nested-blocks-inner-blocks\/#allowed-blocks\">allowedBlocks<\/a><\/code> &#8211; The core or custom blocks that are allowed to be used within the InnerBlock<\/li><li><code><a href=\"https:\/\/developer.wordpress.org\/block-editor\/how-to-guides\/block-tutorial\/nested-blocks-inner-blocks\/#template\">template<\/a><\/code> &#8211; The initial layout for the blocks when there is no content.<\/li><li><code><a href=\"https:\/\/github.com\/WordPress\/gutenberg\/blob\/654bace6e3c7269dfdf99d78b0e08ea8e63376f8\/packages\/block-editor\/src\/components\/inner-blocks\/README.md#templatelock\">templateLock<\/a><\/code> &#8211; This allows the flexibility for the block. In the example, &#8220;all&#8221; means that it is fully locked, and the user cannot make modifications to add\/remove blocks or move them around. The link shows the other settings for the <code>templateLock<\/code>.<\/li><li><code><a href=\"https:\/\/developer.wordpress.org\/block-editor\/how-to-guides\/block-tutorial\/nested-blocks-inner-blocks\/#orientation\">orientation<\/a><\/code> &#8211; This one isn&#8217;t in the example, but is worth mentioning. This gives the direction (horizontal\/vertical) the blocks are laid out. This property controls the orientation of the &#8220;move block&#8221; arrows that appear on the Block Toolbar for each child block inside the Inner Blocks component.<\/li><li><code><a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/trunk\/packages\/block-editor\/src\/components\/inner-blocks#renderappender\">renderAppender<\/a><\/code> &#8211; If you want to customize the &#8220;Add a block&#8221; experience, this is the setting for you. The documentation is sparse, but basically you can display the plus icon, nothing at all, or customize the look:<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" src=\"\/gutenberg\/files\/2025\/01\/image.png\" alt=\"\" class=\"wp-image-500\" width=\"403\" height=\"125\" srcset=\"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/image.png 789w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/image-636x199.png 636w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/image-768x240.png 768w\" sizes=\"(max-width: 403px) 100vw, 403px\" \/><\/figure>\n\n\n\n<p><code>save.js<\/code> is more straightforward:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"419\" src=\"\/gutenberg\/files\/2025\/01\/SCR-20250123-phhz-1024x419.png\" alt=\"\" class=\"wp-image-473\" srcset=\"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-phhz-1024x419.png 1024w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-phhz-636x260.png 636w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-phhz-768x314.png 768w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-phhz-1536x628.png 1536w, https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/files\/2025\/01\/SCR-20250123-phhz.png 1912w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>All the save function requires is the <code>InnerBlocks.Content<\/code> tag, and it will render on the frontend.<\/p>\n\n\n\n<p><strong>Dynamic Blocks: <\/strong>It&#8217;s important to note that in a dynamic block with a PHP template you still need to have a save function in Javascript to save the InnerBlocks content. You don&#8217;t have to save all of the rest of the markup of your parent block, but the InnerBlocks.Content has to saved so that the markup and block grammar of the child blocks is saved to the post_content. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2>useInnerBlockProps<\/h2>\n\n\n\n<p>With <a href=\"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/handbook\/block-api\/\">Block API 2<\/a>, we can have greater control over the markup of the Inner Blocks component in the editor by using the <code>useInnerBlockProps<\/code> hook. This is the inner blocks version of the <code>useBlockProps<\/code> hook. <a href=\"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/handbook\/block-wrapper-props-useblockprops\/\">See the handbook page on that hook. <\/a><\/p>\n\n\n\n<p>In the edit function import the hook from the <code>@wordpress\/block-editor<\/code> package: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { useBlockProps, useInnerBlocksProps } from '@wordpress\/block-editor';<\/code><\/pre>\n\n\n\n<p>In your edit function define <code>useInnerBlockProps<\/code> and pass it an object with any of the parameters you want to output on the HTML element that <code>useInnerBlockProps<\/code> is spread on. These are optional, but most often you&#8217;ll want to use this for modifying the <code>className<\/code>. Or adding data attributes.<br><br>Then on the HTML element use the <code>...<\/code><a rel=\"noreferrer noopener\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/Spread_syntax\" target=\"_blank\">spread syntax<\/a> to output those parameters on the HTML markup.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export function BlockEdit() {\n    const blockProps = useBlockProps();\n    const innerBlockProps = useInnerBlocksProps({\n        style: {\n          color: '#222222',\n          backgroundColor: '#eeeeee',\n          padding: '15px'\n        },\n        className: isSelected ? 'additional-class' : '',\n        'data-test': 'data attributes'\n    });\n\n    return (\n        &lt;div {...blockProps}>\n            &lt;div {...innerBlockProps} \/>\n        &lt;\/div>\n    )\n};<\/code><\/pre>\n\n\n\n<p>In Save.js you&#8217;ll want to do the same as above if using <code>useInnerBlocksProps<\/code> but you&#8217;ll add the <code>.save()<\/code> method to the end when calling it. Like so: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const innerBlockProps = useInnerBlocksProps.save({\n        style: {\n          color: '#222222',\n          backgroundColor: '#eeeeee',\n          padding: '15px'\n        },\n        className: isSelected ? 'additional-class' : '',\n        'data-test': 'data attributes'\n    });<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/handbook\/block-wrapper-props-useblockprops\/\">See the page on useBlockProps<\/a> for more details and documentation.<\/p>\n\n\n\n<h3>Accessing Data Between Blocks<\/h3>\n\n\n\n<h4>Child block data from Parent<\/h4>\n\n\n\n<p>Accessing the data of the child blocks from the parent via JavaScript is straightforward, via the <a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/packages\/packages-data\/\">@wordpress\/data<\/a> module:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const { __ } = wp.i18n;\nconst { useSelect } = wp.data;\nconst { store: blockEditorStore } = wp.blockEditor;\n\n\/**\n * Block Edit Component\n *\/\nconst Edit = (props) => {\n\tconst { clientId } = props;\n\tconst innerBlocks = useSelect(\n\t\t(select) => select(blockEditorStore).getBlock(clientId).innerBlocks,\n\t);\n\n\tconsole.log( innerBlocks ); \/\/ This will log all the inner blocks on your browser console\n};<\/code><\/pre>\n\n\n\n<p>For dynamic blocks, using the <a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_block\/\"><code>WP_Block<\/code><\/a> class in <code>render.php<\/code> will give you access to the child data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\/**\n * Render Custom Block\n *\n * @param  array    $atts    Block Attributes\n * @param  string   $content Block Content (InnerBlocks)\n * @param WP_Block $block_class Block Class Instance\n * @return string          Block Markup\n *\/\nfunction render( $atts, $content, $block_class ) {\n\t\/\/ Check how many inner blocks are available\n\t$item_count = $block_class->inner_blocks->count();\n\tif ( empty( $item_count ) ) {\n\t\treturn '';\n\t}\n\tob_start();\n\t?>\n\t&lt;div>\n\t\t&lt;?php\n\t\tforeach( $block_class->inner_blocks as $_block ) {\n\t\t\t\/\/ Get the inner block data\n\t\t\t$inner_block = $block_class->inner_blocks->current(); \n\t\t\t\n\t\t\t\/\/ Holds the inner block attribute\n\t\t\t$attribute   = $inner_block->attributes;\n\n\t\t\t\/\/ This will display the attributes data\n\t\t\tvar_dump( $attribute );\n\n\t\t\t\/\/ increase the index in the WP_Block_List class used to retrieve the current block\n\t\t\t$block_class->inner_blocks->next();\n\t\t}\n\n\t\t\/\/ reset the index in the WP_Block_List class to the initial state\n\t\t$block_class->inner_blocks->rewind();\n\t\t?>\n\t&lt;\/div>\n\t&lt;?php\n\treturn ob_get_clean();\n}<\/code><\/pre>\n\n\n\n<h4>Adding Context<\/h4>\n\n\n\n<p>Using the Block Context feature, you can specify what data can transverse between Inner Blocks. <\/p>\n\n\n\n<p>From within <code>registerBlockType<\/code>, add a <code>providesContext<\/code> block to the block that wants to share data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>registerBlockType( 'my-plugin\/record', {\n    \/\/ ... cut ...\n\n    attributes: {\n        recordId: {\n            type: 'number',\n        },\n    },\n\n    providesContext: {\n        'my-plugin\/recordId': 'recordId',\n    },\n...<\/code><\/pre>\n\n\n\n<p>In the receiving block, add a <code>useContext<\/code> statement:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>registerBlockType( 'my-plugin\/record-title', {\n    title: 'Record Title',\n    category: 'widgets',\n\n    usesContext: &#91; 'my-plugin\/recordId' ],\n    edit( { context } ) {\n        return 'The record ID: ' + context&#91; 'my-plugin\/recordId' ];\n    },\n...<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>For all the php folks out there:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>register_block_type( 'my-plugin\/record-title', array(\n    'render_callback' => function( $attributes, $content, $block ) {\n        return 'The current record ID is: ' . $block->context&#91;'my-plugin\/recordId'];\n    },\n) );<\/code><\/pre>\n\n\n\n<p>For more information and a complete example, see the Developer Reference Guide on <a href=\"https:\/\/developer.wordpress.org\/block-editor\/reference-guides\/block-api\/block-context\/#example\">Context<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Inner blocks, or nested blocks, is a component from the @wordpress\/block-editor package that allows a parent block to use other child blocks for its structure. These child blocks in the Inner Blocks component can be free form, allowing the child blocks to be rearranged, create multiples of, or locked into a specific pattern. There is [&hellip;]<\/p>\n","protected":false},"author":3670,"featured_media":0,"parent":575,"menu_order":12,"comment_status":"closed","ping_status":"closed","template":"","meta":[],"status-is-a-reserved-term":[],"assignee":[],"_links":{"self":[{"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/pages\/110"}],"collection":[{"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/users\/3670"}],"replies":[{"embeddable":true,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/comments?post=110"}],"version-history":[{"count":19,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/pages\/110\/revisions"}],"predecessor-version":[{"id":527,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/pages\/110\/revisions\/527"}],"up":[{"embeddable":true,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/pages\/575"}],"wp:attachment":[{"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/media?parent=110"}],"wp:term":[{"taxonomy":"status-is-a-reserved-term","embeddable":true,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/status-is-a-reserved-term?post=110"},{"taxonomy":"assignee","embeddable":true,"href":"https:\/\/id-developer-upgrade-58.cms-devl.bu.edu\/gutenberg\/wp-json\/wp\/v2\/assignee?post=110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}