To add interactions to blocks using the Interactivity API, developers can use:
- Directives: Added to the markup to add specific behavior to the DOM elements of the block
- Store: Contains the logic and data (state, actions, side effects, etc.) needed for the behavior
DOM elements are connected to data stored in the state and context through directives. If data in the state or context change directives will react to those changes, updating the DOM accordingly (see diagram).

What are directives?
Directives are custom attributes that are added to the markup of your block to add behavior to its DOM elements. This can be done in the render.php file (for dynamic blocks) or the save.js file (for static blocks).
Interactivity API directives use the data- prefix. Here’s an example of directives used in HTML markup.
<div
data-wp-interactive="myPlugin"
data-wp-context='{ "isOpen": false }'
data-wp-watch="callbacks.logIsOpen"
>
<button
data-wp-on--click="actions.toggle"
data-wp-bind--aria-expanded="context.isOpen"
aria-controls="p-1"
>
Toggle
</button>
<p id="p-1" data-wp-bind--hidden="!context.isOpen">
This element is now visible!
</p>
</div>
Directives can also be injected dynamically using the HTML Tag Processor.
With directives, you can directly manage interactions such as side effects, state, event handlers, attributes, or content.
List of Directives
wp-interactive
The wp-interactive directive “activates” the interactivity for the DOM element and its children through the Interactivity API (directives and store). The directive includes a namespace to reference a specific store, that can be set as a string or an object.
<!-- Let's make this element and its children interactive and set the namespace -->
<div
data-wp-interactive="myPlugin"
data-wp-context='{ "myColor" : "red", "myBgColor": "yellow" }'
>
<p>
I'm interactive now,
<span data-wp-style--background-color="context.myBgColor"
>and I can use directives!</span
>
</p>
<div>
<p>
I'm also interactive,
<span data-wp-style--color="context.myColor"
>and I can also use directives!</span
>
</p>
</div>
</div>
<!-- This is also valid -->
<div
data-wp-interactive='{ "namespace": "myPlugin" }'
data-wp-context='{ "myColor" : "red", "myBgColor": "yellow" }'
>
<p>
I'm interactive now,
<span data-wp-style--background-color="context.myBgColor"
>and I can use directives!</span
>
</p>
<div>
<p>
I'm also interactive,
<span data-wp-style--color="context.myColor"
>and I can also use directives!</span
>
</p>
</div>
</div>
data-wp-interactive is a requirement for the Interactivity API “engine” to work. In the following examples the data-wp-interactive has not been added for the sake of simplicity. Also, the data-wp-interactive directive will be injected automatically in the future.
wp-context
It provides a local state available to a specific HTML node and its children.
The wp-context directive accepts a stringified JSON as a value.
// render.php
<div data-wp-context='{ "post": { "id": <?php echo $post->ID; ?> } }' >
<button data-wp-on--click="actions.logId" >
Click Me!
</button>
</div>
See store used with the directive above
store( 'myPlugin', {
actions: {
logId: () => {
const { post } = getContext();
console.log( post.id );
},
},
} );
Different contexts can be defined at different levels, and deeper levels will merge their own context with any parent one:
<div data-wp-context='{ "foo": "bar" }'>
<span data-wp-text="context.foo"><!-- Will output: "bar" --></span>
<div data-wp-context='{ "bar": "baz" }'>
<span data-wp-text="context.foo"><!-- Will output: "bar" --></span>
<div data-wp-context='{ "foo": "bob" }'>
<span data-wp-text="context.foo"><!-- Will output: "bob" --></span>
</div>
</div>
</div>
wp-bind
This directive allows setting HTML attributes on elements based on a boolean or string value. It follows the syntax data-wp-bind--attribute.
<li data-wp-context='{ "isMenuOpen": false }'>
<button
data-wp-on--click="actions.toggleMenu"
data-wp-bind--aria-expanded="context.isMenuOpen"
>
Toggle
</button>
<div data-wp-bind--hidden="!context.isMenuOpen">
<span>Title</span>
<ul>
SUBMENU ITEMS
</ul>
</div>
</li>
See store used with the directive above
store( 'myPlugin', {
actions: {
toggleMenu: () => {
const context = getContext();
context.isMenuOpen = ! context.isMenuOpen;
},
},
} );
The wp-bind directive is executed:
- When the element is created
- Each time there’s a change on any of the properties of the
stateorcontextinvolved in getting the final value of the directive (inside the callback or the expression passed as reference)
When wp-bind directive references a callback to get its final value:
- The
wp-binddirective will be executed each time there’s a change on any of the properties of thestateorcontextused inside this callback. - The returned value in the callback function is used to change the value of the associated attribute.
The wp-bind will do different things when the DOM element is applied, depending on its value:
- If the value is
true, the attribute is added:<div attribute> - If the value is
false, the attribute is removed:<div> - If the value is a string, the attribute is added with its value assigned:
<div attribute="value" - If the attribute name starts with
aria-ordata-and the value is boolean (eithertrueorfalse), the attribute is added to the DOM with the boolean value assigned as a string:<div aria-attribute="true">
wp-class
This directive adds or removes a class to an HTML element, depending on a boolean value. It follows the syntax data-wp-class--classname.
<div>
<li
data-wp-context='{ "isSelected": false }'
data-wp-on--click="actions.toggleSelection"
data-wp-class--selected="context.isSelected"
>
Option 1
</li>
<li
data-wp-context='{ "isSelected": false }'
data-wp-on--click="actions.toggleSelection"
data-wp-class--selected="context.isSelected"
>
Option 2
</li>
</div>
See store used with the directive above
store( 'myPlugin', {
actions: {
toggleSelection: () => {
const context = getContext();
context.isSelected = ! context.isSelected;
},
},
} );
The wp-class directive is executed:
- When the element is created
- Each time there’s a change on any of the properties of the
stateorcontextinvolved in getting the final value of the directive (inside the callback or the expression passed as reference)
The boolean value received by the directive is used to toggle (add when true or remove when false) the associated class name from the class attribute.
It’s important to note that when using the wp-class directive, it’s recommended to use kebab-case for class names instead of camelCase. This is because HTML attributes are not case-sensitive, and HTML will treat data-wp-class--isDark the same as data-wp-class--isdark or DATA-WP-CLASS--ISDARK.
So, for example, use the class name is-dark instead of isDark and data-wp-class--is-dark instead of data-wp-class--isDark:
<!-- Recommended -->
<div data-wp-class--is-dark="context.isDarkMode">
<!-- ... -->
</div>
<!-- Not recommended -->
<div data-wp-class--isDark="context.isDarkMode">
<!-- ... -->
</div>
/* Recommended */
.is-dark {
/* ... */
}
/* Not recommended */
.isDark {
/* ... */
}
wp-style
This directive adds or removes inline style to an HTML element, depending on its value. It follows the syntax data-wp-style--css-property.
<div data-wp-context='{ "color": "red" }'>
<button data-wp-on--click="actions.toggleContextColor">
Toggle Color Text
</button>
<p data-wp-style--color="context.color">Hello World!</p>
</div>
See store used with the directive above
store( 'myPlugin', {
actions: {
toggleContextColor: () => {
const context = getContext();
context.color = context.color === 'red' ? 'blue' : 'red';
},
},
} );
The wp-style directive is executed:
- When the element is created
- Each time there’s a change on any of the properties of the
stateorcontextinvolved in getting the final value of the directive (inside the callback or the expression passed as reference)
The value received by the directive is used to add or remove the style attribute with the associated CSS property:
- If the value is
false, the style attribute is removed:<div> - If the value is a string, the attribute is added with its value assigned:
<div style="css-property: value;">
wp-text
It sets the inner text of an HTML element.
<div data-wp-context='{ "text": "Text 1" }'>
<span data-wp-text="context.text"></span>
<button data-wp-on--click="actions.toggleContextText">
Toggle Context Text
</button>
</div>
See store used with the directive above
store( 'myPlugin', {
actions: {
toggleContextText: () => {
const context = getContext();
context.text = context.text === 'Text 1' ? 'Text 2' : 'Text 1';
},
},
} );
The wp-text directive is executed:
- When the element is created
- Each time there’s a change on any of the properties of the
stateorcontextinvolved in getting the final value of the directive (inside the callback or the expression passed as reference)
The returned value is used to change the inner content of the element: <div>value</div>.
wp-on
wp-on-async instead if your directive code does not need synchronous access to the event object. If synchronous access is required, consider implementing an async action which yields to the main thread after calling the synchronous API.
This directive runs code on dispatched DOM events like click or keyup. The syntax is data-wp-on--[event] (like data-wp-on--click or data-wp-on--keyup).
<button data-wp-on--click="actions.logTime" >
Click Me!
</button>
See store used with the directive above
store( 'myPlugin', {
actions: {
logTime: ( event ) => {
console.log( new Date() );
},
},
} );
The wp-on directive is executed each time the associated event is triggered.
The callback passed as the reference receives the event (event), and the returned value by this callback is ignored.
wp-on-async
This directive is a more performant approach for wp-on. It immediately yields to main to avoid contributing to a long task, allowing other interactions that otherwise would be waiting on the main thread to run sooner. Use this async version whenever there is no need for synchronous access to the event object, in particular the methods event.preventDefault(), event.stopPropagation(), and event.stopImmediatePropagation().
wp-on-window
wp-on-async-window instead if your directive code does not need synchronous access to the event object. If synchronous access is required, consider implementing an async action which yields to the main thread after calling the synchronous API.
This directive allows you to attach global window events like resize, copy, and focus and then execute a defined callback when those happen.
List of supported window events.
The syntax of this directive is data-wp-on-window--[window-event] (like data-wp-on-window--resize