InnerBlocks

Allows blocks inside a block. Kind of works like the editor itself, just inside a block. Used in the core “Media & Text” block for example. Can also be used to create a connected continuous block as the core “Group” block for example.

Example with the regular InnerBlocks:

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';

//styles that make it look good in the editor
import './editor.scss';

const BLOCKNAME = "inner-blocks";
const BLOCKPATH = `wp-gb/${BLOCKNAME}`;

const STYLES = {
	boxShadow: "1px 1px 1px 0px rgba(0, 0, 0, 0.4)",
	minHeight: 100,
	padding: "48px 48px 0 48px",
	display: "flex",
	alignItems: "center",
	justifyContent: "center",
	backgroundColor: "white",
	color: "black"
};

registerBlockType( BLOCKPATH, {
	apiVersion: 2,
	title: __( 'Inner Blocks', 'wp-gb' ),
	description: __( 'The description' ),
	category: 'wp-gb',
	icon: 'smiley',

	edit: (props) => {
		const ALLOWED_BLOCKS = [
			'core/image',
			'core/paragraph',
			'core/columns',
			'core/heading',
			'wp-gb/inner-blocks'
		];

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

		return (
			<div { ...useBlockProps({ style: STYLES	}) }>
				<InnerBlocks
					allowedBlocks={ ALLOWED_BLOCKS }
					template={ TEMPLATE }
				/>
			</div>
		)
	},

	save: (props) => {
		return (
			<div { ...useBlockProps.save({ style: STYLES }) }>
				<div>
					<InnerBlocks.Content />
				</div>
			</div>
		)
	},
} );

Another header and another paragraph to the block then this can be added by clicking on the appender button as the header image illustrated:

The inner block of an inner block can also be an inner block.

RenderAppender

Use the renderAppender prop in order to create a connected continuous block:

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';

//styles that make it look good in the editor
import './editor.scss';

const BLOCKNAME = "inner-blocks-appender";
const BLOCKPATH = `wp-gb/${BLOCKNAME}`;

const STYLES = {
	boxShadow: "1px 1px 1px 0px rgba(0, 0, 0, 0.4)",
	minHeight: 100,
	padding: "48px 48px 0 48px",
	display: "flex",
	alignItems: "center",
	justifyContent: "center",
	backgroundColor: "white",
	color: "black"
};

registerBlockType( BLOCKPATH, {
	apiVersion: 2,
	title: __( 'Inner Blocks With Appender', 'wp-gb' ),
	description: __( 'The description' ),
	category: 'wp-gb',
	icon: 'smiley',

	edit: (props) => {
		const ALLOWED_BLOCKS = [
			'core/button'
		];

		const TEMPLATE = [ [ 'core/button' ] ];

		return (
			<div { ...useBlockProps({ style: STYLES	}) }>
				<InnerBlocks
					allowedBlocks={ ALLOWED_BLOCKS }
					template={ TEMPLATE }
					orientation="vertical"
					renderAppender={ () => (
						<InnerBlocks.ButtonBlockAppender />
					) }
				/>
			</div>
		)
	},

	save: (props) => {
		return (
			<div { ...useBlockProps.save({ style: STYLES }) }>
				<div>
					<InnerBlocks.Content />
				</div>
			</div>
		)
	},
} );

The renderAppender cb function could also return a custom element instead of the “ButtonBlockAppender” or “DefaultBlockAppender” components.

Pass down props

Props can also be passed down to the child of an InnerBlock using the providesContext and usesContext attributes. The child components inside the block can then use the attribute context from props and refer to the parent attribute that is passed down. Here’s an example:

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { TextControl } from '@wordpress/components';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';

//styles that make it look good in the editor
import './editor.scss';

const BLOCKNAME = "inner-blocks-passing-props";
const BLOCKPATH = `wp-gb/${BLOCKNAME}`;

registerBlockType( BLOCKPATH, {
	apiVersion: 2,
	title: __( 'Inner Blocks (passing props)', 'wp-gb' ),
	description: __( 'The description' ),
	category: 'wp-gb',
	icon: 'smiley',
	attributes: {
		blockTitle: {
			type: "string",
			default: __( 'Parent Title (given here)', 'wp-gb' ),
		}
	},
	providesContext: {
		'parentTitle': 'blockTitle',
	},


	edit: ({attributes, setAttributes}) => {

		const TEMPLATE = [ [ 'wp-gb/child-block', {} ] ];

		return (
			<div { ...useBlockProps() }>
				<h1>Block title:</h1>
				<TextControl
					value={attributes.blockTitle}
					onChange={(newValue) => {
						setAttributes({blockTitle: newValue})
					}}
				/>
				<InnerBlocks template={ TEMPLATE } />
			</div>
		)
	}
} );

registerBlockType( "wp-gb/child-block", {
	apiVersion: 2,
	title: __( 'Child Block', 'wp-gb' ),
	description: __( 'The description' ),
	category: 'wp-gb',
	icon: 'smiley',
	usesContext: [ 'parentTitle' ],

	edit: ({context}) => {

		return (
			<div { ...useBlockProps() }>
				<h2>{context['parentTitle']}</h2>
			</div>
		)
	}
} );

The save function can’t use the context attribute, so the chosen context needs to be stored as an attribute to be used in the save function.

useInnerBlocksProps

Inner blocks can also use something similar to the useBlockProps function where the wrapper element can be controlled. This function is experimental and is called useInnerBlocksProps. Here’s an example:

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks, __experimentalUseInnerBlocksProps as useInnerBlocksProps } from '@wordpress/block-editor';

//styles that make it look good in the editor
import './editor.scss';

const BLOCKNAME = "inner-blocks-with-inner-block-props";
const BLOCKPATH = `wp-gb/${BLOCKNAME}`;

const STYLES = {
	minHeight: 100,
	padding: "48px 48px 0 48px",
	color: "black"
};


registerBlockType( BLOCKPATH, {
	apiVersion: 2,
	title: __( 'Inner Blocks With Inner Block Props', 'wp-gb' ),
	description: __( 'The description' ),
	category: 'wp-gb',
	icon: 'smiley',

	edit: (props) => {
		const ALLOWED_BLOCKS = [
			'core/paragraph',
			'core/image',
		];

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

		const blockProps = useBlockProps({ style: STYLES });

		const innerBlockProps = useInnerBlocksProps( {
			ref: blockProps.ref,
			className: "unique-class"
		}, {
			allowedBlocks: ALLOWED_BLOCKS,
			template: TEMPLATE,
		});

		return (
			<div { ...blockProps }>
				<div { ...innerBlockProps }>
					{ innerBlockProps.children }
				</div>
			</div>
		)
	},

	save: (props) => {
		return (
			<div { ...useBlockProps.save({ style: STYLES }) }>
				<div className="unique-class">
					<InnerBlocks.Content />
				</div>
			</div>
		)
	},
} );

GitHub URL
Classification

Category

Block Editor