ACF has always been my go-to plugin for building blocks and custom fields in WordPress. Since integrating with the Gutenberg editor, building nearly-native blocks has never been easier.

However, there are some cases where the native Gutenberg editing experience just feels a lot more intuitive. An example of this is the button element.

Comparing the ACF Button with the Gutenberg button

ACF Button (Link field):

Screenshot of an ACF link field.

Looks a bit old-school now, very limited in what you can do with it.

The (better) Gutenberg button field:

Screenshot of a button in Gutenberg

With the ability to edit the appearance:

Screenshot of the appearance settings in Gutenberg

So it got me thinking, how do I add native buttons inside an ACF block template? A quick google search brought me to Bill Erickson’s post about InnerBlocks which you can read here: https://www.billerickson.net/innerblocks-with-acf-blocks/

What is InnerBlocks

InnerBlocks is part of Gutenberg which simply is a new tag for creating nested blocks. With ACF 6, you can now use it inside the block template.

ACF 6.0 includes ACF Blocks Version 2. This next generation of ACF Blocks brings us much closer to the native block experience

https://www.advancedcustomfields.com/resources/whats-new-with-acf-blocks-in-acf-6/

Creating an InnerBlocks template

Add 'jsx' => true in your supports array to enable the tag:

<?php
acf_register_block_type(
		array(
			'name'            => 'block-example',
			'title'           => __( 'Block Example' ),
			'render_template' => 'partials/blocks/block-example.php',
			'supports'        => array(
				'jsx'   => true,
			),
		)
	);
?>

Include <InnerBlocks /> where you want to add your native Gutenberg elements:

<div class="block-example">
	<div class="block-inner">
		<InnerBlocks />
	</div>
</div>

Now, you will be able to use any Gutenberg block within your custom ACF block.

Changing the layout based on ACF fields

The InnerBlocks tag accepts a “template” attribute which lets you define which fields are shown when the block is loaded on the page.

We are going to create 2 layouts:

  • Layout 1 contains a heading and a paragraph
  • Layout 2 contains a paragraph and a button.

Create the ACF field

  1. Create an ACF Select field on the block-example block called Block Variation (block_variation)
  2. Create two choices – layout_1 and layout_2

Get the value of the selection

<?php
$block_variation = get_field( 'block_variation' );

Build the template:

<?php
/**
 * Example of InnerBlocks with ACF and Gutenberg.
 *
 * @author Anthony Vickery-Hartnell <anthony@webassembler.co.uk>
 * @package webassembler
 */

// Create variation options.
$layout_1 = array(
	array(
		'core/heading',
		array(
			'level'         => 3,
			'aligned'       => false,
			'placeholder'   => 'Placeholder text',
			'textAlignment' => 'center',
			'className'     => 'has-text-align-center',
		),
	),
	array(
		'core/button',
		array(
			'placeholder' => 'Button text',
			'url'         => '',
		),
	),
);

$layout_2 = array(
	array(
		'core/paragraph',
		array(
			'placeholder' => 'This is a paragraph.',
		),
	),
	array(
		'core/button',
		array(
			'placeholder'   => 'Button text',
			'url'           => '',
			'className'     => 'is-content-justification-left',
			'textAlignment' => 'left',
		),
	),
);

$block_variation = get_field( 'block_variation' );
$template        = '';

if ( 'layout_1' === $block_variation ) {
	$template = $layout_1;
} elseif ( 'layout_2' === $block_variation ) {
	$template = $layout_2;
}
?>

<div class="block-example">
	<div class="block__inner">
		<InnerBlocks
			template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>"
		/>
	</div>
</div>

Now, when you load the page and use the Block Variation dropdown, you’ll see the Gutenberg editor live reload the new templates inside your block. This now feels like the native block experience, while still giving you the PHP based templating language you know as a WordPress developer.

Note – I found out that switching variations actually clears the content each time because the content itself is only saved into the block once the page is saved.

Limit which blocks can be selected inside InnerBlocks

You can go a step further and determine which blocks are allowed to be used within the template with the allowed_blocks attribute.

<?php
$allowed_blocks = array( 'core/button', 'core/paragraph' );
?>
<div class="block-example">
	<div class="block-inner">
		<InnerBlocks allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"/>
	</div>
</div>

To get a list of all the available blocks, look here: https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src

In the link above, every folder is a block so just open the block.json file to find all the attributes that you can use in the template:

Screenshot of the Gutenberg Github repo.
Example of the core/heading block.json settings

Note – native blocks will be prefixed with core/ (e.g. core/heading) and every ACF block will be prefixed with acf/ (e.g. acf/block-example).

Wrapping it up with a final template

<?php
/**
 * Example of InnerBlocks with ACF and Gutenberg.
 *
 * @author Anthony Vickery-Hartnell <anthony@webassembler.co.uk>
 * @package webassembler
 */

$block_classes = array( 'block_example' );

if ( ! empty( $block['className'] ) ) {
	$block_classes = array_merge( $block_classes, explode( ' ', $block['className'] ) );
}

// Add block classes if the background colour is set on the block.
if ( ! empty( $block['backgroundColor'] ) ) {
	$block_classes[] = 'has-background';
	$block_classes[] = 'has-' . $block['backgroundColor'] . '-background-color';
}

// Add block classes if the text colour is set on the block.
if ( ! empty( $block['textColor'] ) ) {
	$block_classes[] = 'has-text-color';
	$block_classes[] = 'has-' . $block['textColor'] . '-color';
}

// Change the layout based on the selected variation.
$layout_1 = array(
	array(
		'core/heading',
		array(
			'level'         => 3,
			'aligned'       => false,
			'placeholder'   => 'Placeholder text',
			'textAlignment' => 'center',
			'className'     => 'has-text-align-center',
		),
	),
	array(
		'core/button',
		array(
			'placeholder' => 'Button text',
			'url'         => '',
		),
	),
);

$layout_2 = array(
	array(
		'core/paragraph',
		array(
			'placeholder' => 'This is a paragraph.',
		),
	),
	array(
		'core/button',
		array(
			'placeholder'   => 'Button text',
			'url'           => '',
			'className'     => 'is-content-justification-left',
			'textAlignment' => 'left',
		),
	),
);

$block_variation = get_field( 'block_variation' );
$template        = '';
$allowed_blocks  = array();

if ( 'layout_1' === $block_variation ) {

	$template        = $layout_1;
	$block_classes[] = 'layout-1';
	$allowed_blocks  = array( 'core/button', 'core/heading' );

} elseif ( 'layout_2' === $block_variation ) {

	$template        = $layout_2;
	$block_classes[] = 'layout-2';
	$allowed_blocks  = array( 'core/button', 'core/paragraph' );

}
?>
<div class="<?php echo esc_attr( join( ' ', $block_classes ) ); ?>">
	<div class="block__inner">
		<InnerBlocks
			template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>"
			allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"
			templateLock="all"
		/>
	</div>
</div>

I’ve added to the template above to output classes on the block div wrapper based on the selection and any native classes applied when selecting background text and colour.

The templateLock="all" attribute locks the template fields in place so you can’t accidentally delete theme. You can only delete them once you’ve unticked the padlock icon just for that block.

If this is something you’d like to add to your site or you’re a fellow developer who needs some help, feel free to get in touch.

Back to blog