The Interactivity API was included in WordPress 6.4 as a private API that could only be used by Core blocks. With the release of WordPress 6.5, the API has been made public and is now available for developers to use in their custom projects.
Table of Contents
Until recently, Gutenberg development primarily focused on enhancing the backend of blocks, which mainly affected content creators. The Interactivity API, on the other hand, focuses on the frontend.
Typically, users interact with the website by clicking or scrolling through the page. The Interactivity API allows you to add behaviors to blocks that will happen in response to user interactions. These may include simple effects, such as showing or hiding elements on click or animating elements on scroll, as well as more advanced features like navigation without page reloads or instant search.
A practical example of a feature built with the Interactivity API is the lightbox option in the Core Image block, added in WordPress 6.4.
Traditionally, features like this one have a long history of being coded with jQuery, and even today, many themes and plugins continue to rely on it. The Interactivity API aims to decrease jQuery usage in WordPress projects and become the new standard for coding interactive elements.
General overview of the Interactivity API
The Interactivity API shares many similarities with other JS frameworks, especially Alpine.js, which makes it accessible for anyone already familiar with the modern JavaScript ecosystem.
It extends standard HTML with directives, which are special data attributes that can listen to and modify the behavior of any DOM element. With directives, you can manipulate the DOM, apply CSS styles, handle user input, and much more.
It’s important to highlight that the block creation workflow doesn’t change. To add interactivity to a block you need to extend it with new features of the Interactivity API. You still can use the @wordpress/create-block scaffolding tool and develop the editor part of the block just as you normally do.
To add interactivity to blocks using the Interactivity API, you need to:
- Add directives as data attributes to the HTML markup to add specific behaviors to the block. You would usually add them in the
render.php
file within the block directory (only dynamic blocks are supported in WordPress 6.5). The list of all publicly available directives can be found in the Interactivity API Reference. - Create a store with the logic (state, actions, or callbacks) needed for interactivity. Typically, it is added in the
view.js
file within the block directory. In the store, actions can be defined as functions that run in response to user interaction, such as a click. Actions update the global state or the local context, which, in turn, updates the HTML element connected to either of them. Besides actions, you can also define callbacks that would run as side effects. Callbacks are also functions, but are not directly invoked by user interactions, they are invoked by the state change. You can read more about the store in the documentation.
The following section of this article will demonstrate how to code a block for a charity that allows users to calculate how many trees will be planted for the donation they plan to make. By the end, you should have a good grasp of how to use the Interactivity API.
This is what you’re going to build:
Prerequisites
This tutorial expects that you are familiar with the fundamentals of building custom blocks, such as development environment, file structure, block.json
metadata, static vs. dynamic rendering of a block, etc. It also assumes that you’ve already built at least a basic block. If this is not the case, I would recommend reading the Build your first block tutorial first.
Creating a basic toggle block
The first step is to scaffold the initial block structure using the Interactivity API template @wordpress/create-block-interactive-template. It’s best to run it from the /plugins
directory in a local WordPress installation.
npx @wordpress/create-block@latest donation-calculator --template @wordpress/create-block-interactive-template
Next, use the command cd donation-calculator
to navigate to the block directory and start the development with npm start
.
The scaffolding tool has created a basic interactive toggle block, which should be available for use in the editor after the plugin is activated in the WordPress admin panel.
Let’s now inspect the generated code to understand what makes this block interactive.
Open the block.json
file in the/src
folder. In the supports
section, you should see that the interactivity
is set to true
. This line is necessary to enable the Interactivity API in the block. Also, ensure that the viewScriptModule
property is defined, and the path to the view.js
file is correct.
"supports": {
"interactivity": true
},
"viewScriptModule": "file:./view.js"
wp-interactive directive
Now open the render.php
file with the frontend markup of the block.
<?php
$unique_id = wp_unique_id( 'p-' );
?>
<div
<?php echo get_block_wrapper_attributes(); ?>
data-wp-interactive="create-block"
<?php echo wp_interactivity_data_wp_context( array( 'isOpen' => false ) ); ?>
data-wp-watch="callbacks.logIsOpen"
>
<button
data-wp-on--click="actions.toggle"
data-wp-bind--aria-expanded="context.isOpen"
aria-controls="<?php echo esc_attr( $unique_id ); ?>"
>
<?php esc_html_e( 'Toggle', 'donation-calculator' ); ?>
</button>
<p
id="<?php echo esc_attr( $unique_id ); ?>"
data-wp-bind--hidden="!context.isOpen"
>
<?php
esc_html_e( 'Donation Calculator - hello from an interactive block!', 'donation-calculator' );
?>
</p>
</div>
A few data attributes have been added to the HTML markup. These are the directives that were mentioned earlier in the article. They all follow data-wp-directiveName
syntax.
The wp-interactive directive activates interactivity for the wrapping <div>
and its children through the Interactivity API. It includes a namespace create-block
to reference the store defined in the view.js
file.
import { store, getContext } from '@wordpress/interactivity';
store( 'create-block', {
actions: {
toggle: () => {
const context = getContext();
context.isOpen = ! context.isOpen;
},
},
callbacks: {
logIsOpen: () => {
const { isOpen } = getContext();
// Log the value of `isOpen` each time it changes.
console.log( `Is open: ${ isOpen }` );
},
},
} );
The namespace in the wp-interactive
directive should match the first argument of the store function. You can change the create-block
namespace to the one that matches the plugin name – the donation-calculator
in this example. This change needs to be updated in both files – render.php
and view.js
.
wp-context directive
The interaction in this block is quite common – it shows or hides the paragraph when the user clicks on the button. To store information about the paragraph’s visibility, you need a context. With the Interactivity API, you can use wp_interactivity_data_wp_context() function for this, which can be added to the wrapping <div>
. The context is local and is available to <div>
and all its children, but not to other blocks.
The wp_interactivity_data_wp_context()
function returns a stringified JSON of a context directive. In this block, wp-context
stores information about the visibility of the paragraph under the isOpen
key, with the initial value of false
. This value will later be used to conditionally add or remove the hidden
attribute from the paragraph in response to user interaction.
<div
<?php echo get_block_wrapper_attributes(); ?>
data-wp-interactive="donation-calculator"
<?php echo wp_interactivity_data_wp_context( array( 'isOpen' => false ) ); ?>
data-wp-watch="callbacks.logIsOpen"
>
wp-on directive
Next, an event listener needs to be added to the button to handle the click. The Interactivity API provides a wp-on directive to listen to DOM events.
The directive follows the syntax data-wp-on--[event]
.
In this example, the data-wp-on--click
directive is added to the button. The name of the function, toggle
, declared in the store as an action, is passed as a value.
<button
data-wp-on--click="actions.toggle"
data-wp-bind--aria-expanded="context.isOpen"
aria-controls="<?php echo esc_attr( $unique_id ); ?>"
>
<?php esc_html_e( 'Toggle', 'donation-calculator' ); ?>
</button>
Next, the logic needs to be created – what should happen when the user clicks the button. Now, because the Interactivity API follows a declarative approach, the event triggered by user interaction doesn’t directly modify the HTML element. Instead, it should update the state or the context, and the Interactivity API will take care of updating the element connected to either of them.
In this block, when the user clicks on the button, it triggers an action that sets the isOpen
context to the opposite value (eg., from false
to true
). The Interactivity API then automagically shows or hides the <p>
element.
Let’s open the view.js
file and inspect the toggle()
function defined in the actions
property of the store.
store( 'donation-calculator', {
actions: {
toggle: () => {
const context = getContext();
context.isOpen = ! context.isOpen;
},
},
});
The toggle()
function first retrieves the current context properties with getContext()
and then sets the isOpen
property to the opposite value. That’s all the function does.
To verify if the logic works as expected, you can add console.log( context.isOpen );
under the last line of the toggle()
function. Now, when you click on the button, you should see in the console that the value changes with every click.
The Interactivity API also provides directives for window and document event listeners. For further details, you can refer to the documentation.
wp-bind directive
To make the toggle block work, you need to connect the paragraph that you want to show or hide to the isOpen
context. You use the wp-bind directive, which allows setting HTML attributes on elements based on a boolean or string value.
The directive follows the syntax data-wp-bind--[attribute]
.
In this example, a hidden
attribute should be added to the paragraph only when the isOpen
context is set to false
. So the data-wp-bind--hidden
attribute needs to be added to the <p>
element with the opposite value of context.isOpen
assigned to it.
<p
id="<?php echo esc_attr( $unique_id ); ?>"
data-wp-bind--hidden="!context.isOpen"
>
<?php
esc_html_e( 'Donation Calculator - hello from an interactive block!', 'donation-calculator' );
?>
</p>
When isOpen
is set to false
, the hidden
attribute will be added to <p>
, so it will no longer be visible. When it is set to true
, the hidden
attribute will be removed and the paragraph will be visible again.
wp-class directive
Alternatively, you could hide the paragraph by adding a class .hidden
, which would have a CSS property display
set to none
. The Interactivity API includes a wp-class directive that adds or removes a class conditionally based on a boolean value.
The directive follows the syntax data-wp-class--[classname]
.
Let’s go ahead and replace the data-wp-bind--hidden
attribute on the paragraph with data-wp-class--hidden
, and leave the value as it is. You also need to add the .hidden
{display: none}
code to the style.scss
file in the block directory.
<p
id="<?php echo esc_attr( $unique_id ); ?>"
data-wp-class--hidden="!context.isOpen"
>
<?php
esc_html_e( 'Donation Calculator - hello from an interactive block!', 'donation-calculator' );
?>
</p>
When isOpen
is set to false, the class="hidden"
attribute will be added to <p>
, making it no longer visible. When it is set to true, the class will be removed.
wp-style directive
Another approach to hiding the paragraph would be to add an inline style to the paragraph, with the value of display:none
. The Interactivity API includes a wp-style directive that adds or removes inline styles from the element.
The directive follows the syntax data-wp-style--[css-property]
.
To use the display
CSS property, you need to add the data-wp-style--display
directive to the <p>
element and set it to either none
or block
. You can’t use the context.isOpen
directly as value because it returns true
or false
, but you can use it as a base for a derived state that will be computed from it.
The Interactivity API store has another property called state, where you can define the derived state or context using a getter function. Let’s create a display()
getter. It will return none if context.isOpen
is false
and block
when true
.
store( 'donation-calculator', {
state: {
get display() {
const context = getContext();
return context.isOpen ? 'block' : 'none';
}
},
actions: {
toggle: () => {
const context = getContext();
context.isOpen = ! context.isOpen;
}
},
});
You can then use the display()
getter in the data-wp-style--display
directive, treating it like any other state property, referring to it as state.display
. Let’s add it to the paragraph.
<p
id="<?php echo esc_attr( $unique_id ); ?>"
data-wp-style--display="state.display"
>
<?php
esc_html_e( 'Donation Calculator - hello from an interactive block!', 'donation-calculator' );
?>
</p>
Creating a donation calculator
Let’s modify the block to create a donation calculator for a charity organization. It will have a form where site visitors can enter the amount they are willing to donate. The calculator will then calculate and display the number of trees the organization can plant for the specified donation. The block will allow the website admin to set the price of planting one tree, which will be used in the calculations.
There are a few steps that you can take:
- define an attribute that will store the price (
block.json
) - add a control where the editor can set the price for planting one tree and include a visual preview of a form (
edit.js
) - make a few changes to the HTML markup of the block, such as adding a form, adding the Interactivity API directives etc. (
render.php
) - define an action and getter functions that will handle calculations (
view.js
) - add the style for the block (
style.scss
andeditor.scss
)
Adding price control in the editor
First, define a price
attribute to store the price of planting a tree set by the person editing the page. Add the attributes
property to the existing elements of the block.json
. The price
attribute will have a type of number
and a default value which will be used by the calculator in case the editor won’t set the price.
"attributes": {
"price": {
"type": "number",
"default": 15
}
},
Next, create the editor view for the block. Add this code to the edit.js
file.
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { __experimentalNumberControl as NumberControl, PanelBody
} from '@wordpress/components';
export default function Edit( { attributes, setAttributes } ) {
const { price } = attributes;
const exampleDonationAmount = 1000;
const trees = Math.floor( exampleDonationAmount / price );
return (
<div { ...useBlockProps() }>
<InspectorControls>
<PanelBody title={ __( 'Calculator settings' ) }>
<NumberControl
label={ __( 'Set the price for one tree' ) }
help={ __( 'The value will be used for calculations.' ) }
value={ price }
min={ 1 }
onChange={
( value ) =>
setAttributes( {
price: Number( value )
} )
}
/>
</PanelBody>
</InspectorControls>
<form className="calculator">
<label for="contribution-value" className="calculator-label">{ __( 'Check the impact of your donation:' ) }</label>
<div class="calculator-input">$
<input disabled value={ exampleDonationAmount } className="calculator-input-form" type="number" id="contribution-value"/>
</div>
<output className="calculator-output">
{[
__( 'Your ' ),
<span>${ exampleDonationAmount }</span>,
__( ' donation will enable us to plant ' ),
<span>{ trees }</span>,
__( ' trees.' )
]}
</output>
</form>
</div>
);
}
You won’t be able to add the Interactivity API to the backend of the block in WordPress 6.5. It will serve as a preview of the form with some predefined data and a control for the price. The calculator will only work on the front end for site visitors.
Adding styles to the block
You also need to add some styles to the block. Go ahead and copy the following CSS and add it to the style.scss
file in the block directory.
.wp-block-create-block-donation-calculator {
box-sizing: border-box;
background-color: #f2fcf6;
color: #023a51;
border: 3px solid #023a51;
border-radius: 1.5rem;
text-align: center;
overflow: hidden;
.calculator {
padding: 3rem 2rem;
&-input {
margin: 1.25rem auto;
font-size: 1.75rem;
color: #023a51;
display: flex;
justify-content: center;
align-items: center;
&-form{
padding: 0.5rem 1rem;
margin-left: 0.5rem;
border: 2px solid #023a51;
border-radius: 1rem;
background-color: #fff;
font-size: 1.5rem;
color: #023a51;
max-width: 130px;
}
}
&-output {
display: none;
&.show {
display: block;
}
span {
color: #f2fcf6;
background: #0cab49;
font-weight: bold;
border-radius: 5px;
padding: 0.25rem 0.5rem;
}
}
}
}
Add the style for the backend (in the editor.scss
) to make the info paragraph visible at all times in the editor.
.wp-block-create-block-donation-calculator {
.calculator-output {
display: block;
}
}
The editor view of the block:
Creating the frontend view of the block
The price control in the editor allows the user to set the price of planting a tree. The value is stored in the price
attribute, which can be accessed in the render.php
file through the $attributes
array, under its key, as defined in block.json
.
You can access the price via $attributes['price']
, which stores the user-entered value or the default of 15
.
The price is passed to the context so that the Interactivity API can use it. You can use the wp_interactivity_data_wp_context() function to set the initial context for the price
and for the contribution
, which will store the value typed by the user in the input field.
Remove everything from the render.php
file and add new markup for the calculator form.
<?php
$context = array(
'price' => (int)$attributes['price'],
'contribution' => 0
);
?>
<div
<?php echo get_block_wrapper_attributes(); ?>
data-wp-interactive="donation-calculator"
<?php echo wp_interactivity_data_wp_context( $context ); ?>
>
<form aria-label="<?php esc_attr_e( 'Calculate the impact of your donation.' ); ?>" class="calculator">
<label for="contribution-value" class="calculator-label"><?php esc_html_e( 'Check the impact of your donation:' ); ?></label>
<div class="calculator-input">$
<input
data-wp-on--input="actions.calculate"
placeholder="0"
type="number"
id="contribution-value"
class="calculator-input-form"
>
</div>
</form>
</div>
The frontend of the block should now look like this:
Notice the data-wp-on--input="actions.calculate"
directive added to the input element.
Once the user starts typing, the calculate()
function defined under the actions property of the store
will be invoked. The number typed into the form is passed to the function as an event object (e) and accessed as e.target.value
property. The calculate()
function will save the value in the context.contribution
property.
The short info box should show up under the form, stating how many trees can be planted for the amount the user is willing to donate. These two values can be derived from the context.contribution
. The first one (number of trees) is calculated by dividing the donation value by the price and the second one (the value with a $ sign) is created by converting the number to a string.
Lastly, the form output is only displayed once the user enters a number greater than 0 in the form. The value of the style is derived from the context.contribution
.
Under the state property, let’s add the following getter functions:
donation()
: returns the formatted value with the $ sign as a stringtrees()
: returns the result of dividing the value by the price, rounded downshow()
: returnstrue
if the user-entered value is greater than 0, orfalse
otherwise
This logic needs to be added in the view.js
file.
import { store, getContext } from '@wordpress/interactivity';
store( 'donation-calculator', {
state: {
get donation() {
const context = getContext();
return `$${context.contribution}`;
},
get trees() {
const context = getContext();
return Math.floor( context.contribution / context.price );
},
get show() {
const context = getContext();
return context.contribution > 0;
}
},
actions: {
calculate: ( e ) => {
const context = getContext();
context.contribution = Number( e.target.value );
}
}
} );
wp-text directive
The only task remaining is to display information about the contribution below the form.
The Interactivity API includes a wp-text
directive used to set the inner HTML. You need to add it to the <span>
tags inside the <output>
element.
Then you connect the wp-text
directive to the corresponding state getters, linking the first <span>
in the <output>
to the state.donation
. This displays the user-entered value in the form with a $ sign. The second <span
> is connected to state.trees
displaying the calculated number of trees that can be planted for the donation.
The wp-text
directive manages the setting of the inner HTML for these spans, re-rendering it every time a user types a number into the form field.
To complete this exercise, you will add the data-wp-class--show="state.show"
directive to the output
element. You’ll find the whole code for render.php
below.
<?php
$context = array(
'price' => (int)$attributes['price'],
'contribution' => 0
);
?>
<div
data-wp-interactive="donation-calculator"
<?php echo wp_interactivity_data_wp_context( $context ); ?>
<?php echo get_block_wrapper_attributes(); ?>
>
<form aria-label="<?php esc_attr_e( 'Calculate the impact of your donation.' ); ?>" class="calculator">
<label for="contribution-value" class="calculator-label"><?php esc_html_e( 'Check the impact of your donation:' ); ?></label>
<div class="calculator-input">$
<input
data-wp-on--input="actions.calculate"
placeholder="0"
type="number"
id="contribution-value"
class="calculator-input-form"
>
</div>
<output
class="calculator-output"
data-wp-class--show="state.show"
>
<?php
echo sprintf(
esc_html__( 'Your %s donation will enable us to plant %s trees.' ),
'<span data-wp-text="state.donation"></span>',
'<span data-wp-text="state.trees"></span>'
);?>
</output>
</form>
</div>
The below video shows the completed block on the frontend.
Resources to learn more about the Interactivity API
- API Reference – the official documentation
- WP Briefing discussing the Interactivity API
- Dev Note: Interactivity API in 6.5
- Developer Hours: Exploring the Interactivity API in WordPress 6.5
- Developer Hours: Building custom blocks with the Interactivity API
The Interactivity API is currently under development, with additional features still on the roadmap. For further details, you can refer to this GitHub issue.
Props to @bph, @luisherranz, @cbravobernal, @jonsurrell, @welcher, @greenshady for providing feedback and reviewing this post.
Leave a Reply