Recently, I looked more closely into the new Invoker Commands API and what it means for WordPress.
There is a lot happening on the web platform right now, with tons of new features and capabilities landing in all browsers every month. For example, this January saw Promise.try
and a new Device Posture API to detect devices with foldable screens. Interop 2024 also just concluded, with exciting new additions such as text-wrap: balance
for balanced headlines and Popover for creating overlays declaratively using HTML. Interop 2025 is already coming soon and I am looking forward to seeing more features that help move the web forward.
The Invoker Commands API is one of these new web platform APIs. It provides a way to declaratively assign behaviors to buttons to control interactive elements on the page. Think of a button that allows you to play or pause a video somewhere else, simply by adding some HTML attributes. Naturally, I set out see how I could use this intriguing new API in WordPress.
The Invoker Commands API
The Invoker Commands API is declarative, which means you use HTML attributes to use it, rather than writing imperative JavaScript code. It introduces two new HTML attributes:
commandfor
Add this to a<button>
element and pass the ID of the interactive element to control.command
Specifies the action to perform on the interactive element.
Browsers already support some commands out of the box for native elements like <video>
, <dialog>
or Popover.
Here are a couple of examples:
Controlling a dialog
<button commandfor="mydialog" command="show-modal">Show modal dialog</button>
<dialog id="mydialog">
<button commandfor="mydialog" command="close">Close</button>
Dialog Content
</dialog>
Code language: HTML, XML (xml)
Controlling a video/video
<button type="button" commandfor="my-video" command="play-pause">Play/Pause</button>
<button type="button" commandfor="my-video" command="toggle-muted">Mute/Unmute</button>
<video id="my-video"></video>
Code language: HTML, XML (xml)
Custom commands
The Invoker Commands API also enables you to add custom commands for your own interactive components on a page. They work essentially the same as built-in ones, with the exception that they must start with a double dash (--
). Example:
<button commandfor="my-img" command="--rotate-left">Rotate left</button>
<button commandfor="my-img" command="--rotate-right">Rotate right</button>
<img id="my-img" src="photo.jpg" alt="[add appropriate alt text here]" />
Code language: HTML, XML (xml)
But how do you tell the browser what --rotate-left
and --rotate-right
should do? Here’s where JavaScript comes in.
When you click on the button with such a command, a JavaScript event is dispatched on the controlled element (the invokee), in this case the <img>
element. You can then listen to this event and perform the desired action:
myImg.addEventListener("command", (event) => {
if (event.command == "--rotate-left") {
myImg.style.rotate = "-90deg";
} else if (event.command == "--rotate-right") {
myImg.style.rotate = "90deg";
}
});
Code language: JavaScript (javascript)
Browser support
A quick note on browser support: at the time of writing, this feature is available behind a flag in Chrome, Firefox, and Safari.
A polyfill is also available, and it’s very small (~2KB minified + gzipped).
Introducing Block Invokers
With the basics covered, let’s switch to WordPress. When I first learned about this API and saw the examples, I automatically thought of a button block that controls a video block or a details block. It struck me as a really obvious use case, so I was keen to figure out how to make it happen. Additionally, I was curious to learn how this new feature relates to the Interactivity API in WordPress, which also uses a declarative way to achieve interactivity on a web page.
I created an experimental Block Invokers WordPress plugin and all my findings can be found on GitHub. There you can also find a link to directly test it yourself using WordPress Playground.
Providing invoker commands in a block
I wanted to way to provide information about supported commands during block registration. Imagine this being part of the block.json
metadata:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-plugin/awesome-video",
"title": "Awesome Video",
"category": "media",
"parent": [ "core/group" ],
// ...
"commands": {
"play-pause": {
"label": "Play/Pause",
"description": "If the video is not playing, plays the video. Otherwise pauses it."
},
"toggle-muted": {
"label": "Toggle Muted",
"description": "If the video is muted, it unmutes the video. Otherwise it mutes it."
}
]
}
Code language: JSON / JSON with Comments (json)
To achieve this, I used the block_type_metadata
filter to declare the supported commands for all suitable blocks. For instance:
- The
core/video
andcore/audio
blocks supportplay-pause
,play
,pause
, andtoggle-muted
commands. - The
core/details
block supports thetoggle
,open
, andclose
commands. - The
core/image
block supports custom--show-lightbox
and--hide-lightbox
commands.
In the editor, I then added a new panel to the button block to configure commands. You can select from a list of all existing blocks on the page and their supported commands (if they have any). This way you can say “I want to perform command x on this particular block y.
![Block Invokers plugin UI in the WordPress block editor. It shows a first dropdown to select the block to control. The second dropdown allows you to select the invoker command to perform on the block.](https://i0.wp.com/pascalbirchler.com/wp-content/uploads/2025/02/image.png?fit=270%2C272&ssl=1)
Now, the crucial part is to add a unique ID to both the chosen target block and also the button block, as it is required for the command
attribute to establish the connection between the two elements. The editor stores the ID in a custom block attribute. On the frontend, the plugin adds it to the right elements using the HTML API.
Because Invoker Commands is a declarative API using only HTML attributes, this is already enough to achieve basic interactivity like playing a video, without any JavaScript required.
Reducing the amount of JavaScript needed on websites is a great example of democratizing performance.
Supporting custom invoker commands
However, I also wanted to support custom commands, and for those you need JavaScript event listeners. The image lightbox functionality was an ideal candidate to try this out with dedicated --show-lightbox
and --hide-lightbox
commands.
Here’s where the Interactivity API comes into play. The image block has an extensive and complex store configuration with lots of state, actions, and callbacks. I was able to tap into that and add a new event listener using another HTML attribute: 'data-wp-on--command="actions.handleCommand"
. The action in the block’s view.js
script is then very minimalistic as it can reuse existing logic to perform the right actions depending on the received command:
handleCommand( event ) {
switch ( event.command ) {
case '--show-lightbox':
actions.showLightbox();
break;
case '--hide-lightbox':
actions.hideLightbox();
break;
}
}
Code language: JavaScript (javascript)
This is like the myImg.addEventListener
example earlier, just written in the Interactivity API way.
Now, one could probably implement the previous examples with only the Interactivity API. However, that would require you to load additional JavaScript and write some JavaScript yourself for something that the browser supports out of the box.
Also, it would require you to interact with another block’s data store, which isn’t really meant to be extended by other plugins or provide backward compatibility.
The way I see it, the Invoker Commands API and the Interactivity API can actually work together very well. It allows a block to define a public API of supported interactions that other blocks or components can tap into. These can be either custom actions or default ones supported by the browser. Again, the latter would then work with zero additional JavaScript.
Conclusion
While the Invoker Commands API is still experimental, it already works very well and is very intuitive to use, even in a WordPress context. Blocks are the perfect place to make use of this new technology and I think this should be further explored. The Interactivity API is a great companion to Invoker Commands, so the two are not mutually exclusive.
Browser support is very promising as well, and with the polyfill there is no reason to not look into this new feature already.
My Block Invokers plugin is on GitHub if you want to check it out in more depth.
Leave a Reply