By default a block renders its own markup and nothing else. Inner Blocks turn a block into a container — a block that holds other blocks, the way core/columns holds columns or core/group holds anything you drop inside.
You configure all of this from the Inner Blocks Settings panel in the block editor — no block.json editing required.
Enabling Inner Blocks
In the block editor, switch on the Inner Blocks Settings toggle. This reveals four controls:
Allowed Block Types
Restrict which blocks users can place inside your container. Type a block name (e.g. core/paragraph, core/heading, fancoolo/button) and press Enter to add it.
Leave it blank to allow all blocks.
Block added by default
The template — blocks that are inserted automatically when your block is added to a page. Add block names here and they appear pre-filled, ready to edit. If you set Allowed Block Types, only those names are suggested.
Lock Template
When on, users cannot add, remove, or move the inner blocks — they can only edit the content of the blocks you provided in the template. Useful for fixed layouts where the structure must stay intact.
Parent Block
Restricts where this block can be inserted. See the Parent Block section below — it behaves differently from the other three settings.
Rendering Inner Blocks
Enabling the setting tells WordPress your block accepts children, but you still decide where they appear. In your Content (render.php), echo the $content variable wherever the inner blocks should render:
<div <?php echo get_block_wrapper_attributes(); ?>>
<div class="my-container">
<?php echo $content; ?>
</div>
</div>
$content holds the rendered HTML of everything the user placed inside your block. Without it, the inner blocks are saved but never shown on the front end.
Parent Block
The Parent Block field does the opposite of Allowed Block Types: instead of saying "these blocks may go inside me," it says "I may only be placed inside these parents."
For example, set a block's Parent Block to core/buttons and it becomes a child of the Buttons block — like the native core/button.
Why this needs FanCoolo to do extra work
In plain WordPress, declaring a parent is only half the relationship. A parent like core/buttons keeps its own whitelist of allowed children (it only allows core/button). So if you only declared the parent, your block would be hidden everywhere — the parent still refuses it.
FanCoolo makes the relationship bidirectional automatically. When you set a Parent Block, FanCoolo also adds your block to that parent's list of allowed children, so it actually shows up in the parent's inserter — no custom code needed.
This applies both on the server and inside the editor's inserter (core blocks register their allowed children in JavaScript, which a PHP setting alone can't reach — FanCoolo handles both layers).
Note: FanCoolo only extends a parent that already restricts its children. A parent that allows everything is left untouched, so declaring it as a parent never accidentally narrows what that parent can otherwise hold.