The WordPress Interactivity API (IAPI), introduced in WordPress 6.5, provides a modern, declarative, and performant approach to adding dynamic front-end experiences to blocks. At the heart of this system is a sophisticated, yet simple, state management model that fundamentally shifts how developers approach front-end logic in WordPress. To truly master the IAPI, an in-depth understanding of its three primary state paradigms—Global State, Local Context, and Derived State—is essential. This article will clarify these concepts, detail their implementation, and provide best practices for their strategic use.
The Foundation: Reactive and Declarative State
The IAPI operates on a reactive and declarative principle. State changes automatically trigger updates to the DOM, removing the need for manual DOM manipulation. This is achieved through a set of data-wp-* directives applied directly to HTML elements. The state itself is managed through a simple, proxy-based system that makes mutation straightforward and intuitive.
The core distinction between the three state types lies in their scope and origin:
| State Paradigm | Scope | Primary Use Case | Key Implementation |
|---|---|---|---|
| Global State | Page-wide | Sharing data between disparate blocks (e.g., a site-wide shopping cart total or dark mode setting). | wp_interactivity_state() (PHP) and store() (JavaScript) |
| Local Context | Element and its children (DOM subtree) | Encapsulating independent state for multiple instances of the same block (e.g., individual tab open/closed status). | data-wp-context (HTML) and getContext() (JavaScript) |
| Derived State | Global or Local | Computing values from the existing state to maintain a single source of truth and reduce redundancy. | JavaScript getters within store() and server-side closure functions. |
1. Global State: The Single Source of Truth
Global state serves as the central data repository for all interactive blocks on a page. It is the mechanism for communication between blocks that are not directly related in the DOM hierarchy.
Implementation Details
Global state is typically initialized on the server using the PHP function wp_interactivity_state(), which registers the initial state values under a specific namespace (or “store”):
// Populates the initial global state values for 'myPlugin'.
wp_interactivity_state( 'myPlugin', array(
'isDarkTheme' => true,
'showModal' => false,
'cartCount' => 0,
));
Code language: PHP (php)This server-side initialization is crucial as it allows the IAPI to perform Server-Side Rendering (SSR), ensuring the initial HTML output is correct and interactive even before the JavaScript loads.
On the client-side, the state is accessed and managed using the store() function from @wordpress/interactivity. The state object is a reactive proxy, meaning any mutation to it is automatically tracked and triggers DOM updates:
import { store } from '@wordpress/interactivity';
const { state, actions } = store( 'myPlugin', {
actions: {
toggleModal() {
// Mutating the state directly triggers reactivity
state.showModal = ! state.showModal;
},
addToCart() {
state.cartCount += 1;
}
},
} );
Code language: JavaScript (javascript)In the block’s HTML, global state is accessed via the state object:
<div data-wp-interactive="myPlugin">
<!-- Reads and reacts to state.isDarkTheme -->
<div data-wp-class--is-dark="state.isDarkTheme">
<button data-wp-on-click="actions.toggleModal">
Toggle Modal
</button>
</div>
<!-- Reads and reacts to state.cartCount -->
<span data-wp-text="state.cartCount"></span>
</div>
Code language: HTML, XML (xml)Best Practice: Always initialize all global state on the server via wp_interactivity_state() to leverage SSR and ensure a consistent starting point, even if the data is only used client-side.
2. Local Context: Encapsulation and Isolation
Local context provides a way to manage state that is exclusive to a specific HTML element and its descendants. It is the ideal solution for block-level state that should not interfere with other instances of the same block on the page. Think of it as the block’s private state.
Implementation Details
Local context is defined directly in the HTML using the data-wp-context directive. This directive takes a JSON string of key-value pairs:
<div
data-wp-interactive="myBlock"
data-wp-context='{ "isOpen": false, "activeTab": "details" }'
>
<!-- ... child elements ... -->
</div>
Code language: HTML, XML (xml)For server-side rendering, it is best to use the PHP helper wp_interactivity_data_wp_context() to ensure correct JSON encoding and escaping:
<?php
$context = array( 'isOpen' => false, 'activeTab' => 'details' );
?>
<div
data-wp-interactive="myBlock"
<?php echo wp_interactivity_data_wp_context( $context ); ?>
>
<!-- ... -->
</div>
Code language: JavaScript (javascript)In the HTML, local context is accessed via the context object:
<details data-wp-bind--open="context.isOpen">
<summary data-wp-on-click="context.isOpen = !context.isOpen">
Toggle Details
</summary>
<p>Context is local to this details element.</p>
</details>
Code language: HTML, XML (xml)In JavaScript, context is accessed and updated using the getContext() function, which retrieves the context of the element that triggered the action or callback:
import { getContext } from '@wordpress/interactivity';
store( 'myBlock', {
actions: {
// This action will run in the context of the element that was clicked.
toggle() {
const context = getContext();
context.isOpen = ! context.isOpen;
},
},
} );
Code language: JavaScript (javascript)Context Nesting and Inheritance
A powerful feature of local context is its ability to be nested. When a child element defines its own context, it automatically inherits any properties from its parent’s context. If a property name is duplicated, the child’s context value overrides the parent’s for that subtree.
<div data-wp-context='{ "theme": "light", "level": 1 }'>
<!-- theme: light, level: 1 -->
<div data-wp-context='{ "theme": "dark" }'>
<!-- theme: dark (overridden), level: 1 (inherited) -->
</div>
</div>
Code language: HTML, XML (xml)This nesting capability allows for fine-grained control over state within complex block structures, such as a gallery block where the main block manages a global index (Local Context) and each image within it manages its hover state (Nested Local Context).
3. Derived State: The Computed Property
Derived state is a value that is not stored directly but is instead calculated from other pieces of state—either global state or local context. It is the IAPI’s equivalent of computed properties or selectors in other frameworks.
Implementation Details
Derived state is defined using standard JavaScript getters within the state object of a store. This ensures that the value is only computed when it is accessed, and automatically recomputed whenever its dependencies change.
Consider a global state with a firstName and lastName:
import { store } from '@wordpress/interactivity';
const { state } = store( 'userProfile', {
state: {
firstName: 'Jane',
lastName: 'Doe',
// Derived State: computed from firstName and lastName
get fullName() {
return `${ state.firstName } ${ state.lastName }`;
},
},
} );
Code language: JavaScript (javascript)The fullName property is now part of the state object and can be accessed in HTML directives just like a regular state property:
<p data-wp-text="state.fullName"></p>Code language: HTML, XML (xml)Whenever state.firstName or state.lastName is mutated, the fullName getter is automatically re-evaluated, and any element bound to it is updated.
Derived State from Local Context
Derived state can also depend on the local context. This is achieved by using the getContext() function inside the getter:
import { store, getContext } from '@wordpress/interactivity';
const { state } = store( 'productBlock', {
state: {
// Global state dependency
get isAvailable() {
const { stock } = getContext();
return stock > 0;
},
},
} );
Code language: JavaScript (javascript)In this example, the derived state isAvailable is calculated based on the stock property found in the local context of the element where the action is executed.
Server-Side Derived State
For SSR, derived state can be initialized on the server by calculating the value in PHP. For dynamic values that depend on local context (like in a loop), the wp_interactivity_state() function can accept a PHP closure (an anonymous function) that will be executed during the server-side rendering process:
wp_interactivity_state( 'myProductPlugin', array(
'factor' => 3, // Global State
'product' => function() {
$state = wp_interactivity_state();
$context = wp_interactivity_get_context();
// Calculates product based on local context item and global state factor
return $context['item'] * $state['factor'];
}
));
Code language: PHP (php)This powerful feature allows the server to compute complex values based on the current rendering context, ensuring that the initial HTML output is fully accurate and reactive.
Strategic State Management: When to Choose Which
Choosing the correct state paradigm is the most critical decision for a maintainable and performant interactive block.
| Scenario | Recommended State | Rationale |
|---|---|---|
| Site-wide preference (e.g., dark mode toggle, user login status). | Global State | Requires a single source of truth accessible by all blocks across the page. |
| Block-specific toggle (e.g., a single accordion/tab open state). | Local Context | Each instance of the block must maintain its own independent state without affecting others. |
| Calculating a total (e.g., cart total from an array of items in state). | Derived State | Prevents data redundancy and ensures the total is always synchronized with the underlying item data. |
| A search filter that affects a list block elsewhere on the page. | Global State | The filter block needs to communicate its value to the list block, which may be far away in the DOM. |
| A nested component’s state (e.g., a hover state within a list item). | Nested Local Context | Encapsulates the state to the smallest possible DOM subtree, improving clarity and isolation. |
By meticulously separating concerns—using Global State for shared data, Local Context for isolated block instances, and Derived State for all computed values—developers can build robust, scalable, and highly performant interactive experiences with the WordPress Interactivity API. This architectural clarity is the key to mastering front-end development in the modern WordPress ecosystem.
