WordPress.org

WordPress Developer Blog

Exploring the Block Hooks API in WordPress 6.5

Exploring the Block Hooks API in WordPress 6.5

Introduced in WordPress 6.4, the Block Hooks API is an extensibility mechanism that allows you to dynamically insert blocks into block themes. For example, a plugin could add an eCommerce shopping cart block to a site’s primary navigation menu without requiring users to add it themselves.

The API was designed to emulate hooks and filters you might have used to extend classic themes but with a few key differences.

  • As the name suggests, you can only insert blocks with the Block Hooks API and only in templates, template parts, and patterns.
  • The API works holistically with site editing. While front-end insertion happens automatically when a block is hooked, the user has the ultimate control. In the Site Editor, they can keep, remove, customize, or move the block, and those changes will be reflected on the front end.

WordPress 6.5 extends the API with many new community-requested features. You can insert blocks into navigation menus and user-modified templates, template parts, and patterns. Two new filters allow you to customize an inserted block’s attributes. Statically rendered blocks are now supported, and so much more. 

Through multiple practical examples, this article provides a comprehensive overview of how you can leverage Block Hooks API in your projects. Let’s get started.

How to create block hooks

The concept behind the Block Hooks API is straightforward. You choose a block type to insert into a block theme and another block type as the “anchor.” The hooked block will be inserted relative to the anchor block, often before or after. 

Perhaps you want to hook a Post Featured Image block before the Post Title block. In this case, the anchor is core/post-title, and the relative position is before.

The API includes two methods of implementing block hooks. You can use the blockHooks property in a block’s block.json file or the hooked_block_types filter. Then, the hooked_block filter allows you to modify any previously hooked block. 

While I’ll provide an overview of each method and filter, I encourage you to review the dev notes for a more technical look at the Block Hooks API and the tenets behind its creation. 

block.json

Block hooks specified in block.json are unconditional, meaning the block will be inserted relative to all instances of the anchor block in each template, template part, and pattern.

In a block’s block.json file, include the blockHooks property. This property takes an object where the key is the name of the anchor block, and the value specifies its position. Possible values include before, after, firstChild, and lastChild.

The following example would unconditionally insert the block after all instances of the Post Content block. You can specify more than one anchor.

"blockHooks": {
    "core/post-content": "after"
}

hooked_block_types

The hooked_block_types filter provides more flexibility and is what most examples in this article will use. It lets you hook blocks unconditionally and conditionally. 

The callback function for the filter accepts four parameters and returns the modified array of hooked blocks:

  • $hooked_block_types (array): An array of hooked blocks.
  • $relative_position (string): The relative position of the hooked block: before, after, first_child, or last_child.
  • $anchor_block_type (string): The name of the anchor block.
  • $context (WP_Block_Template|WP_Post|array): The block template, template part, wp_navigation post type, or pattern that the anchor block belongs to.

You will often use the $context parameter when creating conditional hooks. 

Here’s the general structure of the filter and callback function.

function add_example_block_after_post_content_block( $hooked_block_types, $relative_position, $anchor_block_type, $context ) {

	// Unconditionally hook the Example block after the Post Content block.
	if ( 'after' === $relative_position && 'core/post-content' === $anchor_block_type ) {
		$hooked_block_types[] = 'example-block/example-block';
	}

	return $hooked_block_types;
}
add_filter( 'hooked_block_types', 'add_example_block_after_post_content_block', 10, 4 );

hooked_block

The hooked_block filter, and its block-specific variant hooked_block_{$hooked_block_type}, were introduced in WordPress 6.5. With these filters, you can modify the attributes of any hooked block or even wrap it in another block, such as a Group.

The callback function for both filters accepts five parameters and returns the modified parsed hooked block:

  • $parsed_hooked_block (array|null): The hooked block, in parsed block array format, or null, indicating that a previous filter has suppressed the injection of the given block.
  • $hooked_block_type (string): The hooked block type name.
  • $relative_position (string): The relative position of the hooked block (can be one of before, after, first_child, or last_child).
  • $parsed_anchor_block (array): The anchor block, in parsed block array format.
  • $context (WP_Block_Template|WP_Post|array): The block template, template part, wp_navigation post type, or pattern that the anchor block belongs to.

The $parsed_hooked_block and $parsed_anchor_block variables are arrays with the format: 

  • blockName (string): The block name.
  • attrs (array): An array of block attributes.
  • innerBlocks (array): An array of inner blocks, each presented in the same format as $parsed_hooked_block.
  • innerContent (array): An array of inner content.

You probably have never worked with this block representation before. While WordPress has used it internally for years, the Block Hooks API is one of the few tools that exposes the parsed block array format to extenders. 

The most effective way to explore these filters is through examples. The remainder of this article will focus on four different applications of the API that you can then adapt for your own projects.

The basics (Example 1)

For the first example, let’s use the Like Button block developed by Bernie Reiter, one of the lead developers behind Block Hooks. It adds a heart icon and the word “Like.” When clicked, the heart turns red. I will also use the Twenty Twenty-Four theme throughout this article, but feel free to experiment with any block theme. 

The Like Button block is “hooked” in two places by default. Its block.json file includes the following blockHooks property, which will unconditionally insert the block as the lastChild of all Comment Template blocks.

"blockHooks": {
    "core/comment-template": "lastChild"
}

Then, in the plugin’s main file like-button.php, the block is conditionally inserted after the Post Content block using the hooked_block_types filter. For reference, the block’s name is ockham/like-button.

function add_like_button_block_after_post_content_block( $hooked_block_types, $relative_position, $anchor_block_type, $context ) {

	// Only hook the block on Single templates (posts).
	if ( ! $context instanceof WP_Block_Template || ! property_exists( $context, 'slug' ) || 'single' !== $context->slug ) {
		return $hooked_block_types;
	}
	
	// Hook the block after the Post Content block.
	if ( 'after' === $relative_position && 'core/post-content' === $anchor_block_type ) {
		$hooked_block_types[] = 'ockham/like-button';
	}

	return $hooked_block_types;
}
add_filter( 'hooked_block_types', 'add_like_button_block_after_post_content_block', 10, 4 );

In block themes, the Post Content block can appear in many contexts, such as in a Query loop that’s part of a pattern. The plugin’s conditional approach ensures that the Like Button is only inserted after the Post Content block on single.html templates. Nothing happens if $context is not an instance of the WP_Block_Template object or the slug is not single.

With this conditional, the Like Button will be inserted on custom post types unless the theme provides a dedicated single-$posttype-$slug.html template. Depending on your use case, you may need to create stronger conditionals. For a refresher on the template hierarchy, refer to the visual overview in the Theme Handbook.

You also might be tempted to add WordPress conditional tags like is_singlular() and is_user_logged_in() to refine where and when a block is inserted. However, you should avoid this approach. 

While these conditionals will likely work on the front end, they won’t in the Editor. This can lead to unexpected results. It’s best practice to only build conditionals using the parameters provided by the callback function.

In the following screenshot, you can see what this implementation looks like for posts. The Like Button is inserted after the Post Content block and at the bottom of each Comment Template block.