Getting started with the Interactivity API
The Interactivity API was released in WordPress 6.5. It is a new way to write frontend JavaScript specifically designed for the needs of blocks.
This guide will walk you through the basics of the Interactivity API and how to get started with it.
Pre-requisites
The Interactivity API only works in ESM (ECMAScript Modules) environments. This means that you cannot simply use it in a regular viewScript
or JS file that you enqueue in your theme. Instead your build step needs to support this and the file needs to be loaded properly.
Luckily, both 10up-toolkit and @wordpress/scripts handle all of this for you. Just make sure that you use at least version 6.1+ of toolkit or @wordpress/scripts 27.2.0+.
Configuring 10up-toolkit for script modules
In order for toolkit to properly output these script modules you need to enable a new feature flag in your package.json
file. Under the 10up-toolkit
key you need to se the useScriptModules
flag to true
.
{
"10up-toolkit": {
"useScriptModules": true
}
}
With that set you can now start to use the scriptModule
& viewScriptModule
keys in block.json
to define your module scripts. scriptModule
is similar to script
and will load both in the editor and on the frontend. viewScriptModule
is similar to viewScript
and will only load on the frontend. In 90% of cases you will want viewScriptModule
.
If you want to load a module script that is not connected to a block you can also use the moduleEntry
key in package.json
to add your own custom module entrypoints:
{
"10up-toolkit": {
"moduleEntry": {
"my-script": "./src/my-script.js"
}
}
}
These scripts then need to get manually enqueued using the wp_enqueue_script_module
function.
Writing your first Interactivity API code
Now that we are all set up we can get started writing out first Interactivity API powered code.
In this example we are going to write a simple block that renders a toggle that expands and collapses a div when a button is clicked.
First we need to tell WordPress that our block is making use of the Interactivity API by setting supports.interactivity
to true
in the block.json file.
{
"supports": {
"interactivity": true
}
}
This lets WordPress know that there are special html data attributes that need to get processed before the HTML is printed on the page.
Next we can setup the namespace of our interactive region. This is done by adding a data-wp-interactivity
attribute to the root element of our block.
$additional_attributes = [
'data-wp-interactivity' => 'namespace/block-name',
];
?>
<div <?php echo get_block_wrapper_attributes( $additional_attributes ); ?>>
...
</div>
This namespace needs to match the namespace we use in a minute when we add out JS code.
This happens by creating a new file we can for example call view-module.js
. (The name doesn't matter here). This file now needs to get referenced in the viewScriptModule
key in the block.json
file.
{
"viewScriptModule": "file:./view-module.js"
}
In this file we can now import the store
function from the @wordpress/interactivity
package and define out first custom store.
import { store } from '@wordpress/interactivity';
store( 'namespace/block-name', {
...
} );
This store is now ready and connected to our block. From here we can start defining all the logic we need.
Adding a toggle
The Interactivity API is built to be declarative. This means that you define the state of your block and the API takes care of updating the DOM for you. So the HTML you write in the render.php
file really is the source of truth for all the markup that gets displayed. Including it's various interactive states.
This is accomplished by using special html data attributes (directives). WordPress comes with a bunch of different directives. All of then are prefixed with data-wp-
. For example data-wp-class--is-active
can be used to control whether or not the class is-active
is added to an element.
<div data-wp-class--is-active="context.isActive">
...
</div>
In this example the is-active
class would never get added to the div because we don't yet have our context defined. And what the data-wp-class--
directive does is check if the value we reference is truthy and if so it adds the class we define after the --
to the element.
To define out context we can again start in out markup. The data-wp-context
attribute is used to define the initial state of our block. This initial state gets fully server rendered and can then get updated interactively.
$additional_attributes = [
'data-wp-interactivity' => 'namespace/block-name',
'data-wp-context' => wp_json_encode(
[
'isActive' => false,
],
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
];
If you change the value of isActive
in the context the is-active
class will get added or removed from the div.
The Interactivity API two different ways for keeping track of state. Context is used for local state. It is only available to the block itself and any child elements. The DOM is the source of truth here.
There also is state
which is global and shared across all instances of the block and can be accessed by other blocks as well.
In this example we are using context
because we only need the state to be available to the block itself.
State also has one more trick up it's sleve. Unlike context, state also allows you to define derrived state. This is state that is calculated based on other state or even context values. So you can for example access the context
inside a state
getter and return a value based on that.
Lets make it interactive
Now that we have our context defined we can start to add some interactivity to our block. This is done by defining actions that can be triggered by the user.
import { store, getContext } from '@wordpress/interactivity';
store( 'namespace/block-name', {
actions: {
toggle() {
const context = getContext();
context.isActive = ! context.isActive;
},
},
} );
In this example we define a new action called toggle
. This action is now available to be triggered by the user. We can trigger it by adding a data-wp-on--click
attribute to an element in our block.
<?php
$additional_attributes = [
'data-wp-interactivity' => 'namespace/block-name',
'data-wp-context' => wp_json_encode(
[
'isActive' => false,
],
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
];
?>
<div <?php echo get_block_wrapper_attributes( $additional_attributes ); ?>>
<button data-wp-on--click="actions.toggle">
Toggle
</button>
<div data-wp-class--is-active="context.isActive">
...
</div>
</div>
This will now toggle the isActive
state in our context and the is-active
class will get added or removed from the div.
To make this more accessible we can also add a data-wp-bind--
directive and bind the aria-expanded
attribute to this same context.
<?php
$additional_attributes = [
'data-wp-interactivity' => 'namespace/block-name',
'data-wp-context' => wp_json_encode(
[
'isActive' => false,
],
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
];
?>
<div <?php echo get_block_wrapper_attributes( $additional_attributes ); ?>>
<button data-wp-on--click="actions.toggle">
Toggle
</button>
<div data-wp-class--is-active="context.isActive" data-wp-bind--aria-expanded="context.isActive">
...
</div>
</div>
Conclusion
The Interactivity API is a powerful new way to write frontend JavaScript for blocks. It is designed to be declarative and easy to use. This guide has shown you the basics of how to get started with the Interactivity API and how to write your first interactive block.
For more information on the Interactivity API check out the official documentation.