Block variations are one of the most powerful ways to extend WordPress and curate the editing experience for your users. In this article, you will learn how to effectively create block variations and discover ways to incorporate them into your workflows. Let’s get started.
Table of Contents
The Block Variations API
The Block Variations API allows you to create additional versions of existing blocks that differ by a set of initial attributes or inner blocks.
A variation is defined by an object that contains a set of properties. I will cover many of the most important properties in this article, and you can review the rest in the Block Variations API documentation.
- name (type string) – A unique and machine-readable name.
- title (optional, type string) – A human-readable variation title.
- icon (optional, type string | Object) – An icon for the variation in the Editor UI.
- attributes (optional, type Object) – Values that override the original block’s default attributes.
- innerBlocks (optional, type Array[]) – The initial configuration of nested blocks.
- isDefault (optional, type boolean) – Defaults to false. Indicates whether the current variation is the default one.
- isActive (optional, type Function | string[]) – A function or an array of block attributes that are used to determine if the variation is active when the block is selected. The function accepts
blockAttributes
andvariationAttributes
.
Once defined, variations can be created using the function registerBlockVariation()
or unregistered using unregisterBlockVariation()
. The examples below demonstrate how to use both.
Variations can also be declared during the registration of a block. Refer to the Block Registration API for more details. This approach is useful if you are building a custom block and want to provide additional variations.
Getting set up
Block variations are registered in JavaScript, so you first need to create a JavaScript file.
I am going to assume you want to add block variations to a theme, but this approach can be easily adapted to a plugin. The only real difference is where the files and functions are located. For now, let’s create variations.js
and put the file in our theme’s assets/js
folder.
Next, you need to enqueue variations.js
using the enqueue_block_editor_assets
hook and the standard wp_equeue_script()
function. This code should generally be placed in your theme’s functions.php
file unless you have a more advanced setup.
function example_enqueue_block_variations() {
wp_enqueue_script(
'example-enqueue-block-variations',
get_template_directory_uri() . '/assets/js/variations.js',
array( 'wp-blocks', 'wp-dom-ready' ),
wp_get_theme()->get( 'Version' ),
false
);
}
add_action( 'enqueue_block_editor_assets', 'example_enqueue_block_variations' );
Note that wp-blocks
and wp-dom-ready
are listed as dependencies. These will be used to register and unregister the block variations in the following steps, but the setup is complete for now.
The WordPress Developer Blog typically uses ESNext syntax for JavaScript examples. However, since it’s common to add block variations to themes that do not include a build process, the examples here will use plain JavaScript. If your project already has a build process, feel free to use ESNext instead. A great resource for this approach can be found in Beyond block styles, part 1: using the WordPress scripts package with themes.
Creating a block variation
With the variation.js
file enqueued, you can now register block variations using the registerBlockVariation()
function. This function comes from the wp-blocks
dependency that I added to the enqueue function earlier and can be called by prefixing the function with wp.block.
, or like this:
const { registerBlockVariation } = wp.blocks;
registerBlockVariation( ... );
Use whichever method you prefer—I will use wp.blocks.registerBlockVariation()
throughout this article.
The registerBlockVariation()
function accepts the name of the original block and an object that defines the variation you want to create. Let’s look at an example.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'media-text-custom',
title: 'Media & Text Custom',
attributes: {
align: 'wide',
backgroundColor: 'tertiary'
},
}
);
The code above registers a block variation for the Media & Text block (core/media-text
). I have given it a unique name
and a title
, and have set a few attribute values. When this variation is inserted into the Editor, it will be a Media & Text block aligned wide, with a background color set to tertiary
.
You can technically create a block variation without a unique name, although I recommend against this practice. A unique name
allows the Editor to differentiate between your variation and others that may exist.
The variation will appear like this when using the Twenty Twenty-Three theme:
You may have noticed that both the Media & Text block and the “Media & Text Custom” variation appear in the Inserter. This is the right outcome, but you can add the property isDefault: true
to replace the original block with your variation. This method comes in handy when you want to modify the initial state of Core blocks, or third-party blocks.
Let’s update the example so that the variation becomes the default. With this change, Media & Text blocks are always aligned wide and have a background color when inserted.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'media-text-default',
title: 'Media & Text',
attributes: {
align: 'wide',
backgroundColor: 'tertiary'
},
isDefault: true
}
);
Since the variation is now the default, I have changed the title back to “Media & Text” and the Inserter will now only display a single Media & Text block. From a user perspective, they are just using the default block.
There are additional considerations to be aware of when you use isDefault
, especially if the block already has another variation set to isDefault
. Refer to the documentation for more information.
Unregistering block variations
Lots of Core WordPress blocks are actually variations. For instance, the Group block has four variations: Group, Row, Stack, and Grid (experimental). The Embed and Social Icon blocks are also great examples.
Sometimes, you might not need certain block variations on a site. Removing these choices can simplify the Inserter and improve the user experience. To achieve this, use the unregisterBlockVariation
function. Just provide the original block name and the variation name
you want to remove.
The following code unregisters the Stack block.
wp.domReady( () => {
wp.blocks.unregisterBlockVariation( 'core/group', 'group-stack' );
});
Using wp.domReady
is required when unregistering Core blocks. Without it, the function fires too early, and the variation is never removed. While wp.domReady
might not be required for unregistering some third-party blocks, it’s a good idea to use it consistently. Registering block variations does not require wp.domReady
.
Creating a variation with inner blocks
Now let’s return to the Media & Text example above. While this block variation sets the align
and backgroundColor
attributes, what if you want to add default content to the text portion of the block?
We can do this using the innerBlocks
property, which lets you specify an array of blocks to include within the variation upon insertion. You can also set the attributes of these inner blocks.
In the example below, I have added a Header and a Paragraph block to the original example. Each of these also has a placeholder. This will help users create content more consistently.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'media-text-default',
title: 'Media & Text',
attributes: {
align: 'wide',
backgroundColor: 'tertiary'
},
innerBlocks: [
[
'core/heading',
{
level: 3,
placeholder: 'Heading'
}
],
[
'core/paragraph',
{
placeholder: 'Enter content here...'
}
],
],
isDefault: true,
}
);
The block variation will now look like this when inserted:
The innerBlocks
property will only work for blocks that contain other blocks. However, this functionality can be incredibly powerful when combined with block and template locking. While locking is beyond the scope of this article, you can learn more in the Curating the Editor Experience guide.
Using the isActive
property
Next up is the isActive
property, which helps the Editor determine if the variation is active.
To illustrate how this property works, let’s create a new block variation called Text & Media. It will be similar to the previous example, but when inserted, the text will be on the left with the image on the right. This behavior is defined using the attribute mediaPosition: 'right'
.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'text-media',
title: 'Text & Media',
attributes: {
align: 'wide',
backgroundColor: 'tertiary',
mediaPosition: 'right'
}
}
);
The variation will appear in the Inserter alongside the default Media & Text block as expected. However, after inserting the variation, if the user selects the block again, it will be labeled “Media & Text” not “Text & Media”.
This mislabeling occurs because the Editor cannot differentiate between the variation and the original Media & Text block. The problem is exacerbated if the variation has custom description
and icon
properties and can cause confusion for users.
The isActive
property provides a solution and accepts either a function or an array of attribute strings. I will use the array notation here, but you can refer to the Block Variations API documentation for function examples.
Text & Media variation is largely defined by the mediaPosition
attribute, so let’s set isActive: [ ‘mediaPosition’ ]
.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'text-media',
title: 'Text & Media',
attributes: {
align: 'wide',
backgroundColor: 'tertiary',
mediaPosition: 'right'
},
isActive: [ 'mediaPosition' ]
}
);
With this property applied, the Editor will compare the attributes of a selected Media & Text block to any registered variations. If it finds a variation with a mediaPosition
attribute that matches the selected block, mediaPosition: 'right'
in this case, it will understand that the user has selected the Text & Media variation. The descriptive information of the block will be updated accordingly.
If you list multiple attributes in the array, each one will be checked. The variation will only be considered “active” if they all match.
Adding custom icons
To keep things simple, the previous examples didn’t have custom icons. However, giving block variation its own icon is a great way to improve its discoverability in the Inserter. You can assign an icon using the icon
property, which takes a string or an object.
When using a string, it must be associated with a Dashicon. No other strings will work. For example, the Dashicon slug for the WordPress icon is dashicons-wordpress
. Remove the dashicons-
prefix and set icon: ‘wordpress’
to use this icon. The result will look like this:
In most situations, you will probably want to add a custom SVG icon instead of a Dashicon. This can be accomplished by passing an object to the icon
property.
Looking back at the Text & Media example created earlier, the icon does not accurately represent the default state of the block variation. The media should be on the right, not the left.
The icon on the right of the image was created using the original Media & Text icon and a bit of manipulation in Figma. Note that custom icons should generally be 24 pixels square. Below is the SVG code.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21 17.5L21 6L13 6L13 17.5L21 17.5ZM10 14.5L3 14.5L3 16L10 16L10 14.5ZM3 11L10 11L10 12.5L3 12.5L3 11ZM10 7.5L3 7.5L3 9L10 9L10 7.5Z"></path>
</svg>
You can browse the full WordPress icon library in the Components Storybook, and then extract the SVG code using the browser inspector.
The next step is to add this SVG to the icon
property as an object. This notation can be tricky when registering block variations using plain JavaScript instead of ESNext. You can explore the source code for Core blocks on GitHub for plenty of ESNext syntax examples, which will not be covered in this context.
The simplest solution to add the custom SVG using plain JavaScript is to leverage WordPress’s built-in components and functions—similar to what you’re already doing with registerBlockVariation
, unregisterBlockVariation
, and domReady
.
In the enqueue function located in your functions.php
file, add the dependencies for wp-element
and wp-primitives
. You can then use createElement
, SVG
, and Path
to create the icon object.
const textMediaIcon = wp.element.createElement(
wp.primitives.SVG,
{ xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" },
wp.element.createElement(
wp.primitives.Path,
{
d: "M21 17.5L21 6L13 6L13 17.5L21 17.5ZM10 14.5L3 14.5L3 16L10 16L10 14.5ZM3 11L10 11L10 12.5L3 12.5L3 11ZM10 7.5L3 7.5L3 9L10 9L10 7.5Z",
}
)
);
The code might look a bit complicated, but compare it to the raw SVG markup above, and you will be able to spot the similarities.
Finally, set icon: textMediaIcon
, and the result will look like this:
Putting it all together
Combining all of the examples in this article, your final variations.js
file will:
- Add a Media & Text block variation with inner blocks and a colored background that is set to
isDefault
. - Add a Text & Media block variation with a custom icon that demonstrates
isActive
. - Unregister the Core Stack variation of the Group block.
Here is the complete file:
// Register a new default Media & Text block variation.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'media-text-default',
title: 'Media & Text',
attributes: {
align: 'wide',
backgroundColor: 'tertiary'
},
innerBlocks: [
[
'core/heading',
{
level: 3,
placeholder: 'Heading'
}
],
[
'core/paragraph',
{
placeholder: 'Enter content here...'
}
],
],
isDefault: true,
}
);
// Define the icon for the Text & Media block variation.
const textMediaIcon = wp.element.createElement(
wp.primitives.SVG,
{ xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" },
wp.element.createElement(
wp.primitives.Path,
{
d: "M21 17.5L21 6L13 6L13 17.5L21 17.5ZM10 14.5L3 14.5L3 16L10 16L10 14.5ZM3 11L10 11L10 12.5L3 12.5L3 11ZM10 7.5L3 7.5L3 9L10 9L10 7.5Z",
}
)
);
// Register the Text & Media block variation.
wp.blocks.registerBlockVariation(
'core/media-text',
{
name: 'text-media',
title: 'Text & Media',
icon: textMediaIcon,
attributes: {
align: 'wide',
backgroundColor: 'tertiary',
mediaPosition: 'right'
},
isActive: [ 'mediaPosition' ]
}
);
// Unregister the Stack block variation.
wp.domReady( () => {
unregisterBlockVariation( 'core/group', 'group-stack' );
});
Finally, this variations.js
should be enqueued in your theme’s functions.php
file with the necessary dependencies.
function example_enqueue_block_variations() {
wp_enqueue_script(
'example-enqueue-block-variations',
get_template_directory_uri() . '/assets/js/variations.js',
array( 'wp-blocks', 'wp-dom-ready', 'wp-element', 'wp-primitives' ),
wp_get_theme()->get( 'Version' ),
false
);
}
add_action( 'enqueue_block_editor_assets', 'example_enqueue_block_variations' );
This article only scratched the surface of what can be accomplished with block variations, but you now have the basics, so it’s time to start experimenting yourself. Consider how to use variations in your next project and share your implementations with the community.
If you want to see a follow-up to this article with more advanced examples, let me know in the comments.
Leave a Reply