acf_register_block_type()ACF 5.8.0

Registers a custom Gutenberg block through PHP (ACF).

It lets you add a block with its own ACF fields and render template directly from PHP.

After registration, the block can be specified in the ACF location (“Block”) parameter to attach fields to it:

'location' => [
	[
		[
			'param'    => 'block',
			'operator' => '==',
			'value'    => 'acf/hero-block',
		],
	],
],

Block output is handled through a PHP template or callback using get_field() and the_field().

Registration must take place on the acf/init hook.

Since ACF 6.0, it is recommended to register blocks using the new block.json syntax together with register_block_type().

The functionality described in this documentation may not work with modern WordPress versions.

Notes
  • For dynamic rendering, use render_callback, especially when PHP logic is needed.

  • Block previews can be enabled; see below.

  • Nested blocks (innerBlocks) can be used; see below

Returns

(Array|false). An array of the validated settings for the registered block.

Usage

acf_register_block_type( $block );
$block(array) (required)

Block registration arguments (correspond to registerBlockType() parameters in JS).

  • name (String): unique block identifier, for example testimonial.
  • title (String): the display name in the editor.
  • description (String): a short description.
  • category (String): category (common, formatting, layout, widgets, embed).
  • icon (String|Array): Dashicon or SVG; background and foreground colors can be set.
  • keywords (Array): additional search words.
  • post_types (Array): restricts post types.
  • mode (String): auto, preview, edit — interface behavior.
  • align, align_text, align_content (String): default alignment.
  • render_template (String): path to the PHP render template.
  • render_callback (Callable): function/method for rendering the block.
  • enqueue_style, enqueue_script (String): paths to style/script files.
  • enqueue_assets (Callable): function for dynamically registering assets.
  • supports (Array): support options (align, mode, multiple, full_height, jsx, etc.).
  • example (Array): template for a block-inserter preview.

Inner Blocks

<InnerBlocks /> enables nested content (blocks inside blocks).

Use the <InnerBlocks /> component in the block template. It creates an area for inserting nested blocks in the editor.

Important:

  • Only one <InnerBlocks /> is permitted per block.
  • The block must be registered with JSX support — 'jsx' => true; otherwise the component will not work.

<InnerBlocks /> parameters

allowedBlocks(array)

Limits the allowed blocks:

<?php
$allowed_blocks = [ 'core/image', 'core/paragraph' ];
?>
<InnerBlocks allowedBlocks="<?= esc_attr( wp_json_encode( $allowed_blocks ) ) ?>" />
template(array)

Creates the nested-block structure:

<?php
$template = [
	[ 'core/paragraph', [ 'placeholder' => 'Add a root-level paragraph' ] ],
	[ 'core/columns', [], [
		[ 'core/column', [], [
			[ 'core/image', [] ],
		] ],
		[ 'core/column', [], [
			[ 'core/paragraph', [ 'placeholder' => 'Add a inner paragraph' ] ],
		] ],
	] ],
];
?>

<InnerBlocks template="<?= esc_attr( wp_json_encode( $template ) ) ?>" templateLock="all" />
templateLock(string)

Locks template content. Available settings:

  • all — the structure cannot be changed.
  • insert — prevents removal but allows adding new blocks.

See the InnerBlocks component for more information.

Block registration example

add_action( 'acf/init', 'my_acf_init_blocks' );
function my_acf_init_blocks() {
	acf_register_block_type( [
		'name'            => 'restricted',
		'title'           => 'Restricted',
		'description'     => 'A restricted content block.',
		'category'        => 'formatting',
		'mode'            => 'preview',
		'render_template' => 'template-parts/blocks/restricted/restricted.php',
		'supports'        => [
			'align' => true,
			'mode'  => false,
			'jsx'   => true,
		],
	] );
}

The restricted.php template example:

<?php
$classes = trim(
	( $block['className'] ?? '' ) .
	( ! empty( $block['align'] ) ? ' align' . $block['align'] : '' )
);

$start_date = get_field( 'start_date' );
$end_date   = get_field( 'end_date' );

$start_ts = $start_date ? strtotime( $start_date ) : false;
$end_ts   = $end_date ? strtotime( $end_date ) : false;

$notification = 'Content unrestricted.';
if ( $start_date || $end_date ) {
	$notification  = 'Content visible';
	$notification .= $start_date ? " from $start_date" : '';
	$notification .= $end_date ? " until $end_date" : '';
	$notification .= '.';
}
?>

<div class="restricted-block <?= esc_attr( $classes ) ?>">
	<span class="restricted-block-notification"><?= esc_html( $notification ) ?></span>
	<InnerBlocks />
</div>

Block preview

Screenshot of the inserter panel showing a block preview.

To display a preview in the block inserter panel, add the example parameter when registering the block:

acf_register_block_type( [
	'name'        => 'testimonial',
	'title'       => __( 'Testimonial' ),
	'description' => __( 'A custom testimonial block.' ),
	'example'     => [
		'attributes' => [
			'mode' => 'preview',
			'data' => [
				'testimonial' => 'Blocks are...',
				'author'      => 'Jane Smith',
				'role'        => 'Person',
				'is_preview'  => true,
			],
		],
	],
] );

All values in the data array are available to the block template/handler through $block['data'] or get_field().

  • data — contains values available through $block['data'] or get_field().
  • is_preview — is not related to the block and can be used to display alternative markup.
  • preview — the visual presentation mode of the block; edit can also be used, in which case fields associated with the block are displayed.

Changelog

Since 5.8.0 Introduced.

acf_register_block_type() code ACF 6.8.8

function acf_register_block_type( $block ) {
	// Validate block type settings.
	$block = acf_validate_block_type( $block );

	/**
	 * Filters the arguments for registering a block type.
	 *
	 * @since   5.8.9
	 *
	 * @param   array $block The array of arguments for registering a block type.
	 */
	$block = apply_filters( 'acf/register_block_type_args', $block );

	// Require name.
	if ( ! $block['name'] ) {
		$message = __( 'Block type name is required.', 'acf' );
		_doing_it_wrong( __FUNCTION__, $message, '5.8.0' ); //phpcs:ignore -- escape not required.
		return false;
	}

	// Bail early if already exists.
	if ( acf_has_block_type( $block['name'] ) ) {
		/* translators: The name of the block type */
		$message = sprintf( __( 'Block type "%s" is already registered.', 'acf' ), $block['name'] );
		_doing_it_wrong( __FUNCTION__, $message, '5.8.0' ); //phpcs:ignore -- escape not required.
		return false;
	}

	// Set ACF required attributes.
	$block['attributes'] = acf_get_block_type_default_attributes( $block );

	/**
	 * Filters the default ACF block version for blocks registered via acf_register_block_type().
	 *
	 * @since 6.6.0
	 *
	 * @param integer $default_acf_block_version The default ACF block version.
	 * @param array   $block                     An array of block settings.
	 * @return integer
	 */
	$default_acf_block_version = apply_filters( 'acf/blocks/default_block_version', 1, $block );

	if ( ! isset( $block['acf_block_version'] ) ) {
		$block['acf_block_version'] = $default_acf_block_version;
	}

	if ( ! isset( $block['api_version'] ) ) {
		if ( $block['acf_block_version'] >= 3 && version_compare( get_bloginfo( 'version' ), '6.3', '>=' ) ) {
			$block['api_version'] = 3;
		} else {
			$block['api_version'] = 2;
		}
	}

	// Default expanded_editor_buttons to true for V3+ blocks if not explicitly set.
	if ( $block['acf_block_version'] >= 3 && ! isset( $block['expanded_editor_buttons'] ) ) {
		$block['expanded_editor_buttons'] = true;
	}

	// Add to storage.
	acf_get_store( 'block-types' )->set( $block['name'], $block );

	// Handle fields defined inline on the block.
	if ( isset( $block['fields'] ) && is_array( $block['fields'] ) ) {
		$override_group_title = isset( $block['field_group_title'] ) ? (string) $block['field_group_title'] : '';
		acf_register_block_field_group_from_fields( $block['name'], $block['title'], $block['fields'], $override_group_title );
	}

	// Overwrite callback for WordPress registration.
	$block['render_callback'] = 'acf_render_block_callback';

	// Register block type in WP.
	if ( function_exists( 'register_block_type' ) ) {
		register_block_type(
			$block['name'],
			$block
		);
	}

	// Register action.
	add_action( 'enqueue_block_editor_assets', 'acf_enqueue_block_assets' );

	// Return block.
	return $block;
}