WordPress Developer Blog

How to register block variations with PHP

How to register block variations with PHP

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.

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:

	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 the WP_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(
			'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 includes block, inserter, and transform.
  • 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.

Categories: , ,

15 responses to “How to register block variations with PHP”

  1. Pro Avatar

    Can i use it with my existing apk site

    1. Justin Tadlock Avatar

      If this site is built on WordPress, yes.

  2. Kaledri Avatar

    Thanks appriciate it!

  3. Grégoire Noyelle Avatar

    Thank you, Justin.
    Brian made a very good video on how to use this feature with post meta:

  4. Rich Holman Avatar

    This is excellent! I always messed up the Javascript method.

    One variation I often make is the core/group grid variation.

    I tried this here but maybe get_block_type_variations doesn’t yet support core/group?

    if ( 'core/group' === $block_type->name ) {
    $variations[] = array(
    'name' => 'group-grid',
    'title' => 'Group grid',
    'icon' => 'grid-view',
    'attributes' => array(
    'layout' => array(
    'type' => 'grid'

    1. Damien Carbery Avatar

      I too am having an issue with a core/group variation. It doesn’t seem to work in PHP but JS is fine.

      PHP (not working):

      		$variations[] = array(
      			'name'       => 'dcwd-update-info',
      			'title'      => 'Update Info',
      			'icon'       => 'book-alt',
      			'attributes' => array( 'className' => 'is-style-group-info' ),  // add class
      			'innerBlocks' => array(
      				array( 'core/paragraph', array( 'content' =&gt; '<strong>dd MM YY</strong>: information about the update ...', ) ),

      JS (working):

      		name: 'dcwd-group-update',
      		title: 'Update info',
      		attributes: {
      			className: 'is-style-group-info',
      		innerBlocks: [
      					content: '<strong>' + new Date().toLocaleDateString('en-us', { year:"numeric", month:"long", day:"numeric"}) + '</strong>: Info about update...',
    2. Damien Carbery Avatar

      Ouch, my code and ‘strong’ tags got messed up. Here’s the PHP and JS code:

  5. RS Avatar

    This is a great tutorial! It’s working for me, but is there an argument to set the open target for core/buttons? It’s opening the link on the same tab, I want it to open in a new tab, so ‘_blank’.

  6. Kane Hocking Avatar

    Man this would be really helpful because JS has been a huge pain in the ____. Having this would solve that issue and ultimately lead to more better website performances. hahaha

  7. Calculadora Alicia Avatar

    I used it on my WordPress blog and it worked extremely well.

  8. Truck Simulator Avatar

    To register block variations with PHP in WordPress, use the `register_block_type` function, specifying the block name and variations array in the `variations` key within the `attributes` parameter. Customize each variation’s settings like name, title, and attributes.

  9. wamwebdev Avatar

    This is great. Has anyone managed to get this working for ‘core/query’? I understand that the ‘isActive’ may be problematic here(?) as in my JS version I’m using a callback to check both namespace and query.postType, but setting it to just ‘namespace’ should work in principle?

  10. Mahesh Waghmare Avatar
    Mahesh Waghmare

    It not works with PHP though filter `get_block_type_variations` anymore.

    1. Justin Tadlock Avatar

      I just tested this with WordPress 6.6 and can say that it still works as described in the article. If you can share your code, I’m happy to give a test.

  11. Cooper Avatar

    Wow, this is a life saver. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *