WordPress will soon ship with a new method of registering custom block variations. In the past, you would have typically registered them via the JavaScript-based Variations API, but you’ll be able to define variations using a dedicated PHP filter hook in WordPress 6.5.
The hook addition was part of a larger performance-specific patch, which you can read more about in the Dev Note. The great thing for plugin and theme authors is that it makes the process of registering variations easier.
This change also opens more opportunities to integrate with the block system for developers who are less comfortable with JavaScript. So let’s go ahead and dive into what will be possible in WordPress 6.5.
If you’re unfamiliar with block variations, check out An introduction to block variations here on the Developer Blog.
Technically, it’s long been possible to add variations via PHP by injecting them via the register_block_type_args
filter hook, but this method was undocumented and not made clear to developers. The new method in WordPress 6.5 also limits the scope of what you filter since it is specific to block variations rather than the entire block.
Table of Contents
Block variations hook and callback
Technically, WordPress 6.5 doesn’t create a standard set of API functions to use for registering variations. But it does add a new filter hook named get_block_type_variations
, which lets you inject custom variations from either a plugin or theme.
Take a look at the get_block_type_variations
filter hook:
apply_filters(
'get_block_type_variations',
array $variations,
WP_Block_Type $block_type
);
As shown, the hook can pass up to two parameters to any filters that are applied to it:
$variations
: An array of registered variations for the specific block.$block_type
: An instance of theWP_Block_Type
class, representing a registered block type.
When defining a filter for the hook, you will almost always need both of these parameters. So be sure to set the $accepted_args
parameter to 2
when calling the add_filter()
function.
The following is an example of a fictional filter on get_block_type_variations
to familiarize you with how it should be formatted:
add_filter( 'get_block_type_variations', 'themeslug_block_type_variations', 10, 2 );
function themeslug_block_type_variations( $variations, $block_type ) {
if ( 'namespace/block-a' === $block_type->name ) {
// Add variations to the `$variations` array.
} elseif ( 'namespace/block-b' === $block_type->name ) {
// Add more variations to the `$variations` array.
}
return $variations;
}
It’s always necessary to run an if
conditional check to determine whether the block is the one you want to define a variation for. You can use the name
property of the $block
parameter to do this.
Your filter function can also add multiple variations for a single block or define variations for multiple blocks by appending them to the $variations
parameter.
Building a custom variation
Now it’s time to get to the fun part: registering a custom block variation!
For this example, let’s use PHP to recreate a JavaScript-based example from the Variations API Block Editor Handbook documentation. The variation makes a single attribute change: it moves the media section from the Media & Text block to the right.
When inserted into the Block Editor, the variation will look like this:
To create this variation, the first thing you need to do is add a filter on get_block_type_variations
. And because you know the block type you want to target (Media & Text), go ahead and add a check for it by adding this code to either your theme’s functions.php
or your plugin file:
add_filter( 'get_block_type_variations', 'themeslug_block_type_variations', 10, 2 );
function themeslug_block_type_variations( $variations, $block_type ) {
if ( 'core/media-text' === $block_type->name ) {
// Add your variation here.
}
return $variations;
}
Now you must decide which attributes make this variation different from the default block. For the Media & Text block, you can find its available attributes via its block.json
file. In this case, you’ll set the mediaPosition
attribute to right
(it has a default value of left
).
There are several other arguments that you can set for a variation, which we’ll get to. For now, update your themeslug_block_type_variations()
function so that it looks like this:
add_filter( 'get_block_type_variations', 'themeslug_block_type_variations', 10, 2 );
function themeslug_block_type_variations( $variations, $block_type ) {
if ( 'core/media-text' === $block_type->name ) {
$variations[] = array(
'name' => 'themeslug-media-right',
'title' => __( 'Media & Text: Right', 'themeslug' ),
'isActive' => array(
'mediaPosition'
),
'attributes' => array(
'mediaPosition' => 'right'
)
);
}
return $variations;
}
Note the arguments that this variation sets: name
, title
, isActive
, and attributes
. These are the four arguments that I consider “must haves” for nearly every variation.
This is a relatively basic example that illustrates how PHP-based variations work, and you can certainly build more complex variations for your projects. A good exercise beyond this tutorial would be to walk through Building a book review grid with a Query Loop block variation and convert the code to PHP.
Customizing the variation arguments
I won’t dive too deeply into the various arguments for registering custom block variations. These are documented well in the Variations handbook doc. But I do want to list the basic definitions of each and note the differences between using PHP and JavScript.
You can define any of the following arguments for your variation:
name
: A unique identifier for the variation. It’s best practice to prefix this with your theme/plugin slug.title
: An optional human-readable title that should be internationalized.description
: A description of your variation that should be internationalized.category
: A block category to place the variation under if different from the block itself.keywords
: An array of terms that help users discover the variation when searching.icon
: A Dashicon string representing the icon you want to show. Unlike JavaScript-based variations, you cannot assign a custom SVG.attributes
: An array of attributes that define your variation as different from the default.innerBlocks
: The initial configuration of nested blocks (if the block allows nested blocks).example
: An array of structured data used in the block’s preview.scope
: An array of scopes where the variation is applicable, which includesblock
,inserter
, andtransform
.isDefault
: Whether the variation should be the default.isActive
: An array of block attributes that should be used to determine whether the variation is active. Unlike JavaScript-registered variations, you cannot pass a custom callback that determines the active state.
It’s important to reiterate the two major differences between registering with JavaScript and PHP from the above list. First, you cannot use a custom icon
and are limited to the available Dashicons.
But the biggest limitation may be with the isActive
argument. For simple variations, passing an array of attributes to check should be OK. But for more complex variations that require a custom callback to determine the active state, it’s best to use JavaScript.
Outside of those limitations, block variation arguments work exactly the same using PHP as they do with JavaScript.
A future API?
There is currently an open ticket to add a standard register_block_variation()
PHP function, which would be the equivalent of the registerBlockVariation()
JavaScript function. This ticket does not yet have a pull request associated with it, so it’s open for anyone to pick up if they want to include the function in Core.
Until then, the get_block_type_variations
filter hook is always available.
Props to @ndiego and @eidolonnight for editorial review and @ntsekouras and @gziolo for help with a technical question.
Leave a Reply