Inner Blocks - What Are They and How To Use Them

We'll cover some clever patterns and use cases for Inner Blocks.

Steps in this lesson

  1. Enable Inner Blocks
  2. <InnerBlocks> In Your Template
  3. Default Templates
  4. Block Attributes
  5. Allowed Blocks
  6. Lock It Down
  7. Interesting Use Cases

Real Quick, What Are Inner Blocks?

Inner blocks are blocks that can be inserted as children into a parent/container block. Examples would be the Core Group block or the Columns block – these Core blocks allow you to insert other blocks as inner blocks. Your ACF block can also act as a container allowing you to insert Core blocks or even other ACF blocks into the parent block.

"Inner Blocks"

Enable Inner Blocks

When you register your block, add "jsx" => true in the supports object. We've already learned how to register an ACF block, so I'll skip that step here.

"name": "my-block",
"title": "My Block",
"acf": {
"mode": "preview",
"renderTemplate": "blocks/my-block/block.php"
"supports": {
"jsx": true

Note: In order to use InnerBlocks, your block needs to be able to display in `preview` mode. If your parent block has custom fields, those will be accessible in the sidebar. You can still toggle to "edit" mode, but just make sure it's able to display in "preview".

<InnerBlocks> In Your Template

In your render template include the special <InnerBlocks> tag.

<div id="<?php echo $block_id; ?>" class="<?php echo $className; ?>" style="<?= $styles; ?>">
<InnerBlocks />

Where ever you include that tag is where the block editor will render the inner blocks UI within your ACF block.

Let's add some content around the <InnerBlock> tag to better visualize this.

<div id="<?php echo $block_id; ?>" class="<?php echo $className; ?>" style="<?= $styles; ?>">
<p>Content above the inner blocks</p>
<InnerBlocks />
<p>Content below the inner blocks</p>

"Inner Blocks"

The two paragraph tags in the render template are static – they can't be edited in the block editor, but now there's a block inserter UI for inserting inner blocks in between those two static paragraphs.

"Inner Blocks"

Inserting Core blocks into your <InnerBlocks> slot provides the same editing experience as inserting a Core block outside of your block. Every block is editable by clicking it and interacting with the editing UI.

Default Templates

We can define a default template of blocks too! When you insert your block into a page, it will contain a default set of blocks as a starter template.

$template = array(
array('core/heading', array(
'level' => 2,
'content' => 'This is a default heading',
array( 'core/paragraph', array(
'content' => 'This is placeholder paragraph text.',
) )

<InnerBlocks template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>" />

"Inner Blocks"

$template is an array of blocks that gets passed into the template attribute of our <InnerBlocks> tag. Each block is an array itself containing the block's attributes. Depending on the block, different attributes will be available to configure the default state of the block.

Block Attributes

One way I familiarized myself with the attributes available to a block was to insert the block directly into the block editor, configure it, and then toggle the Code Editor view.

"Code Editor"

You can also press ⇧⌥⌘M as a keyboard shortcut to toggle between the Code editor and the Visual editor.

Let's configure a heading block that's an <h3> and has red text:

"Block editor heading"

When we toggle the Code view in the editor, this is what we see:

<!-- wp:heading {"level":3,"textColor":"vivid-red"} -->
<h3 class="has-vivid-red-color has-text-color">This is a heading level 3 with red text</h3>
<!-- /wp:heading -->

There's some inline JSON in there for us to learn about the block attributes to use in our $templates array:

"level": 3,
"textColor": "vivid-red"

You can add to that JSON placeholder to create placeholder text. This is just like placeholder text in a form input. Or you can use content to set actual content in the heading.

Allowed Blocks

Just like $template, we can create an array of blocks to restrict which blocks are allowed to be inserted into our ACF block:

$allowed_blocks = array( 'core/heading', 'core/paragraph', 'core/image' );

template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>"
allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"

The array of allowed blocks is just a simple array of block names. We don't need to include attributes or anything else. You will need to know the official name of the block though. Again, you can toggle the Code editor view and find the name of the block. Core blocks will start with core/ and ACF blocks will start with acf/.

Lock It Down

Finally, we can restrict our ACF block further by either completely locking the block with templateLock="all". This will prevent the editor from inserting, removing, or reordering inner blocks.

template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>"
allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"

You can allow the editor to reorder inner blocks, but prevent inserting or removing inner blocks with templateLock="insert".

template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>"
allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"

Interesting Use Cases

Container Blocks
Most of the time I'm using InnerBlocks for large containers with an intricate background, or some interesting design element that I want to surround my content. In this case, I'd insert my ACF container block, choose the style/background I want from a select list, and then insert the inner blocks.

"Full Screen Container Block"

Other Examples
These are some old examples I shared on Twitter (and here) over a year ago. The way these work is clever. At least I think so.

Tabbed Content or FAQ "Tabbed Inner Blocks" A tab or FAQ block that allows you to insert content that's revealed when you toggle that tab/FAQ. Here's how it works:

There's a parent block called "FAQ" that has an $allowBlocks array that contains one block: "FAQ Item". The only think you can insert into the FAQ parent is an FAQ child.

Both are separate blocks that accept inner blocks. The difference is, the FAQ Child accepts any block you want as a the inner block content. You can even set a $template for the FAQ child – like a heading and some paragraph text. Here's a quick Loom demoing the backend experience of adding an FAQ block. There's usually some JavaScript involved here, unless you use the <details> and <summary> HTML tags. This would probably require a little more effort to make the markup work...

Visibility Block
You create an ACF block that acts as a container. Insert what ever blocks you want as inner blocks while the parent block has fields to set conditions for the block's visibility.

  1. Set the block to be visible based on a future date.
  2. Hide the block unless the user is logged in
  3. Show the block based on the referrer. Coming from Twitter? Here's a special message just for you.

The goal of this post is to help you wrap your mind around inner blocks and get you thinking about what's possible with them. We'll dive in deeper and build some actual blocks together in a future lesson.

🤜 🤛 You did it!

If you've followed along, let me know how this went or if you have any questions.
@Joey_Farruggio or joey@joeyfarruggio.com