Skip to content
Branch: master
Find file History
getdave and talldan Adds Block Appender as placeholder to empty InnerBlocks (#14241)
* Adds Block Appender as placeholder to empty InnerBlocks

Previously when inserting a Block that makes use of InnerBlocks (eg: Columns) it wasn’t clear that the Block had been inserted and by default an empty paragraph Block was inserted.

This changes checks for any innerBlocks and if available disables the default Block insertion (ie: paragraph) and displays the Block Appender as a placeholder. Once a Block is inserted then the placeholder is removed.

* Updates to allow InnerBlocks to control it’s placeholder when empty

Previously all Blocks making use of `InnerBlocks` were defaulted to showing the `BlockListAppender` as the placeholder. This update allows `InnerBlocks` to opt-in to this feature. It does however, require some prop drilling under the hood.

Addresses: #14241 (comment)

* Updates Column Block to opt-in to using Block Appender as placeholder

* Fix edge case where block can be null value

Previously test for presence of child blocks assumed that the block being interogated would always be present. In some circumstances this isn’t true resulting in an error. Fixed to include check for presence of block as if it isn’t there then it cannot have any children (obviously).

* Simplifies wording on Block Appender

* Adds background color to Block Appender

Addresses #14241 (comment)

* Adds support for dark themes

Resolves #14241 (comment)

* Adds keyboard focus support for visual cues

* Fixes comment to approved version

Resolves #14241 (review)

* Adjusts background to use lighter colour variant

Resolves #14241 (comment)

* Adds consistent spacing around appender

This update creates a consistent visual spacing around the appender itself. To handle the edge case of Blocks acting as pass through Blocks (which have negative margins) we have to double the margin on these Blocks.

* Removes unecessary Fragment usage

Resolves #14241 (comment)

* Adjusts font weight and space between icon and “Add Block” text

Addresses part of #14241 (comment)

* Adjusts appender padding to match supplied visual

Addresses visual design as provided in #14241 (comment)

* Aligns appender with sibling Blocks

* Revert "Aligns appender with sibling Blocks"

This reverts commit 49b03ef.

* Updates to use explict and more extenable prop for choosing the placeholder

Previously it was only possible to enable/disable the “Add Block” placeholder. In view of requests to be able to enable yet more states (ie: no Block appender _at all_) the API has been updated to a form which will make it easier to add different placeholders in the future.

* Reverts Column to use default placeholder behaviour

As discuseed, removing this from Column to facilitate merge.

* Removes “is passthrough” edge case for Columns

This reverts commit dc5c352 whilst retaining the consistent spacing around the appender which is still required.

This was reverted because it has been decided that Block List Appender should obey SRP and avoid adding styles to cater of edge case Blocks. Such Block variations should either captured within an API or rather should add overides themselves on an case by case basis.

* Fixes Columns layout edge case for Block Appender

Whilst “block-appender” mode will not be enabled for Columns by default we should cater for this scenario.

* Updates colors for hover and active states

Resolves #14241 (comment)

* Fix docs to show correct variable type

* Updates to appender rendering to utilise render prop

Previously we were constraining control over the type of appenders that could be created. Be utilising an optional render prop we provide developers with the ability to fully customise the render. However, by creating sensible default enum constants within InnerBlocks we preserve the “simple defaults”.

* Adds default appender as explicit constant

Updates to add default “auto insert” appender behaviour as a constant. This way if it is ever removed as the default we have an explicit constant by which to reference this behaviour by which isn’t tied to the term “default”.

* Adds option to hide appender entirely for InnerBlocks if children present

Creates separate prop for InnerBlocks to optionally hide the appender if child Blocks are present. The lower level components have been adjusted with `hideAppender` prop to selectively enable/disable rendering of the appender.

* Updates documentation for `renderAppender` and `hideAppenderWhenChildren`

* Updates to remove superflous usage of `appender` suffix

* Fixes Markdown usage on README heading

* Fix to ensure default appender render when default block is not active

Previously render was returning nothing when `canInsertDefaultBlock` was false (eg: when paragraph Block is disabled but `auto-insert` is still set as the default placeholder when a container Block is inserted. Fixes e2e test failures.

* Exposes placeholder defaults as components on InnerBlocks

Addresses ideas expressed #14241 (review)

Also an `HideWhenChildren` component to control rendering in scenario when children are present. Note that control over this functionality is now soley controlled by the render prop and cannot be activated via a simple prop.

* Adds mixins to handle visually hiding content but preserving for screenreaders

For approach see

* Removes the “Add Block” text from the button appender

Hides text visually but retains it for assistive technologies as we cannot rely on an icon to convey meaning.

Addresses #14241 (comment)

* Adds Button Block Appender component to DRY up component usage

Previously we had duplicates of the Button appender both within `InnerBlocks` and again within the `BlockListAppender` component. Creates a new standalone  `ButtonBlockAppender` component which is used across both components.

Also removes option to provide enum to `InnerBlocks` `renderAppender` prop. Now only render functions are valid. We still expose x2 default “appenders” on `InnerBlocks` to enable easy usage.

* Removes superfluous enum handling on renderAppender prop

* Minor - updates comments to end in full stops

Addresses #14241 (comment)

* Extracts `hideWhenChildBlocks` component for use on `InnerBlocks`

* Updates docs with correct props and usage examples

* Add docs for `ButtonBlockAppender` component

* Adds ability to pass custom CSS className to ButtonBlockAppender component

* Fixes e2e test by ensuring correct className is restored on appender when used inside BlockListAppender

* Revert "Adds mixins to handle visually hiding content but preserving for screenreaders"

This reverts commit 1a9a2f8.

* Utilise existing WP Core screen reader text class

* Removes dependency on compose

* Improves naming of aliased component

In order to avoid inline component functions within `compose` components have been moved to their own functional components and then enhanced. However this causes a naming clash between the base component and the one being created and then enhanced within this file. As a result, all duplicates are aliased with the `Base` prefix to allow a component of the same name to be reused within the same file.

* Capitalise component name

* Correct spelling

* Simplifies check for prop

* Remove `utils` directory and flatten

* Simplify the use of if statements in BlockListAppender

* Use classnames utility to concat classes

* Rename class to block-editor-button-block-appender

* Switch to either arrow functions assigned to a const or exported function declarations

* revert unintentional formatting changes

* Remove HideWhenChildBlocks. This will be added on a separate branch

* Tidy up docs
Latest commit db7decd Apr 12, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
test Move the block components to the block editor module (#14112) Mar 8, 2019
button-block-appender.js Adds Block Appender as placeholder to empty InnerBlocks (#14241) Apr 12, 2019
default-block-appender.js Adds Block Appender as placeholder to empty InnerBlocks (#14241) Apr 12, 2019
index.js Adds Block Appender as placeholder to empty InnerBlocks (#14241) Apr 12, 2019
with-client-id.js Adds Block Appender as placeholder to empty InnerBlocks (#14241) Apr 12, 2019


InnerBlocks exports a pair of components which can be used in block implementations to enable nested block content.

Refer to the implementation of the Columns block as an example resource.


In a block's edit implementation, render InnerBlocks. Then, in the save implementation, render InnerBlocks.Content. This will be replaced automatically with the content of the nested blocks.

import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';

registerBlockType( 'my-plugin/my-block', {
	// ...

	edit( { className } ) {
		return (
			<div className={ className }>
				<InnerBlocks />

	save() {
		return (
				<InnerBlocks.Content />
} );

Note: A block can render at most a single InnerBlocks and InnerBlocks.Content element in edit and save respectively. To create distinct arrangements of nested blocks, create a separate block type which renders its own InnerBlocks and assign as the sole allowedBlocks type.

Note: Because the save step will automatically apply props to the element returned by save, it is important to include the wrapping div in the above simple example even though we are applying no props of our own. In a real-world example, you may have your own attributes to apply to the saved markup, or sibling content adjacent to the rendered nested blocks.



  • Type: Array<String>

Allowed blocks prop should contain an array of strings, each string should contain the identifier of a block. When allowedBlocks is set it is only possible to insert blocks part of the set specified in the array.

const ALLOWED_BLOCKS = [ 'core/image', 'core/paragraph' ];
    allowedBlocks={ ALLOWED_BLOCKS }

The previous code block creates an InnerBlocks area where only image and paragraph blocks can be inserted.

Child blocks that have marked themselves as compatible are not excluded from the allowed blocks. Even if allowedBlocks doesn't specify a child block, a registered child block will still appear on the inserter for this block.

const ALLOWED_BLOCKS = [];
    allowedBlocks={ ALLOWED_BLOCKS }

The previous code block restricts all blocks, so only child blocks explicitly registered as compatible with this block can be inserted. If no child blocks are available: it will be impossible to insert any inner blocks.


  • Type: Array<Array<Object>>

The template is defined as a list of block items. Such blocks can have predefined attributes, placeholder, content, etc. Block templates allow specifying a default initial state for an InnerBlocks area. More information about templates can be found in template docs.

const TEMPLATE = [ [ 'core/columns', {}, [
    [ 'core/column', {}, [
        [ 'core/image' ],
    ] ],
    [ 'core/column', {}, [
        [ 'core/paragraph', { placeholder: 'Enter side content...' } ],
    ] ],
] ] ];
    template={ TEMPLATE }

The previous example creates an InnerBlocks area containing two columns one with an image and the other with a paragraph.


  • Type: Boolean
  • Default: true

If true when child blocks in the template are inserted the selection is updated. If false the selection should not be updated when child blocks specified in the template are inserted.


  • Type: String|Boolean

Template locking of InnerBlocks is similar to Custom Post Type templates locking.

Template locking allows locking the InnerBlocks area for the current template. Options:

  • all — prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them.
  • insert — prevents inserting or removing blocks, but allows moving existing ones.
  • false — prevents locking from being applied to an InnerBlocks area even if a parent block contains locking.

If locking is not set in an InnerBlocks area: the locking of the parent InnerBlocks area is used.

If the block is a top level block: the locking of the Custom Post Type is used.


  • Type: Function
  • Default: - undefined. When renderAppender is not specific the <DefaultBlockAppender> component is as a default. It automatically inserts whichever block is configured as the default block via wp.blocks.setDefaultBlockName (typically paragraph).

A 'render prop' function that can be used to customize the block's appender.


  • For convenience two predefined appender components are exposed on InnerBlocks which can be consumed within the render function:
    • <InnerBlocks.ButtonBlockAppender /> - display a + (plus) icon button that, when clicked, displays the block picker menu. No default Block is inserted.
    • <InnerBlocks.DefaultBlockAppender /> - display the default block appender as set by wp.blocks.setDefaultBlockName. Typically this is the paragraph block.
  • Consumers are also free to pass any valid render function. This provides the full flexibility to define a bespoke block appender.

Example usage

// Utilise a predefined component
	renderAppender={ () => (
		<InnerBlocks.ButtonBlockAppender />
	) }

// Fully custom
	renderAppender={ () => (
		<button className="bespoke-appender" type="button">Some Special Appender</button>
	) }
You can’t perform that action at this time.