Interactivity API Directives
All data-wp-* directive attributes processed by the Interactivity API.
Source: wp-includes/interactivity-api/class-wp-interactivity-api.php
Since: 6.5.0
Directive Syntax
data-wp-{directive}[--{suffix}][---{unique-id}]="[namespace::]{value}"
- directive: The directive name (e.g.,
bind,class,text) - suffix: Modifier for the directive (e.g., attribute name for
bind) - unique-id: Optional unique identifier for multiple same-suffix directives
- namespace: Optional store namespace (defaults to nearest
data-wp-interactive) - value: Reference path or JSON
Reference Paths
Directives reference state and context using dot notation:
state.propertyName → $state_data['namespace']['propertyName']
context.itemName → Current context's ['namespace']['itemName']
state.nested.property → $state_data['namespace']['nested']['property']
!state.isHidden → Negated value
state.items.length → Array count or string length (since 6.8.0)
data-wp-interactive
Defines the namespace for an interactive region.
Since: 6.5.0
Syntax:
<div data-wp-interactive="myPlugin">
<!-- All nested directives use "myPlugin" namespace -->
</div>
<div data-wp-interactive='{"namespace":"myPlugin"}'>
<!-- JSON format also supported -->
</div>
Behavior:
- Sets default namespace for all descendant directives
- Nested
data-wp-interactivecreates new scope - Namespace restored when exiting element
data-wp-context
Provides local context data for an element and its descendants.
Since: 6.5.0
Syntax:
<div data-wp-context='{"isOpen": false, "count": 0}'>
<!-- context.isOpen and context.count available here -->
</div>
<div data-wp-context='otherPlugin::{"id": 123}'>
<!-- Uses explicit namespace -->
</div>
PHP Helper:
<div <?php echo wp_interactivity_data_wp_context( array(
'isOpen' => false,
'count' => 0,
) ); ?>>
Behavior:
- Context is scoped to element and descendants
- Nested contexts merge with parent via
array_replace_recursive() - Access via
context.propertyNamein other directives
data-wp-bind–{attribute}
Dynamically binds an attribute value.
Since: 6.5.0
Syntax:
<a data-wp-bind--href="state.url">Link</a>
<img data-wp-bind--src="state.imageUrl" data-wp-bind--alt="state.imageAlt">
<button data-wp-bind--disabled="state.isLoading">Submit</button>
Behavior:
- Sets attribute to evaluated value
falseremoves non-aria/data attributes- For
aria-*anddata-*attributes, booleans become"true"/"false"strings nullremoves the attribute
Multiple bindings with unique IDs:
<div
data-wp-bind--data-value---1="state.value1"
data-wp-bind--data-value---2="state.value2"
>
data-wp-class–{classname}
Toggles a CSS class based on a boolean value.
Since: 6.5.0
Syntax:
<div data-wp-class--is-open="state.isOpen">
<!-- Has "is-open" class when state.isOpen is truthy -->
</div>
<div data-wp-class--active="context.isActive" data-wp-class--highlighted="state.isHighlighted">
<!-- Multiple class toggles -->
</div>
Behavior:
- Truthy value adds the class
- Falsy value removes the class
- Class name is the suffix after
--
With unique IDs:
<div data-wp-class--featured---abc="state.isFeatured">
<!-- Toggles class "featured---abc" -->
</div>
data-wp-style–{property}
Sets an inline style property.
Since: 6.5.0
Syntax:
<div data-wp-style--color="state.textColor">
<!-- Sets style="color: {value};" -->
</div>
<div
data-wp-style--background-color="state.bgColor"
data-wp-style--font-size="state.fontSize"
>
<!-- Multiple style properties -->
</div>
Behavior:
- Sets the CSS property to evaluated value
- Empty/null/false removes the property
- Merges with existing
styleattribute - Removes entire
styleattribute if empty
data-wp-text
Sets the text content of an element.
Since: 6.5.0
Syntax:
<span data-wp-text="state.message"></span>
<p data-wp-text="context.item.description"></p>
Behavior:
- Replaces all inner content with evaluated value
- Value is HTML-escaped via
esc_html() - Only strings and numbers are rendered
- Other types (null, boolean, array) clear the content
data-wp-each
Iterates over an array, rendering content for each item.
Since: 6.5.0
Since: 6.9.0 — Adds list path to data-wp-each-child.
Syntax:
<template data-wp-each="state.items">
<li data-wp-text="context.item.name"></li>
</template>
<!-- With custom item name -->
<template data-wp-each--product="state.products">
<div data-wp-text="context.product.title"></div>
</template>
Requirements:
- Must be on a
<template>tag - Value must evaluate to a list (indexed array)
- Template content must start and end with tags (no loose text)
Context Variable:
- Default:
context.item - With suffix:
context.{suffix}(e.g.,data-wp-each--product→context.product) - Suffix is converted from kebab-case to camelCase
data-wp-each-child:
Rendered items automatically receive data-wp-each-child="{namespace}::{path}" attribute. This marks them as generated content to prevent re-processing.
Skipped Cases:
- Non-template elements
- Associative arrays (objects in JS)
- Template content with top-level text nodes
- Elements with existing
data-wp-each-child(manual SSR)
data-wp-router-region
Marks a region for client-side navigation updates.
Since: 6.5.0
Syntax:
<main data-wp-router-region="main-content">
<!-- This region updates during client-side navigation -->
</main>
Behavior:
- First occurrence triggers setup (once per page)
- Enqueues loading bar animation styles
- Adds loading bar markup to footer via
wp_footer - Works with
@wordpress/interactivity-routermodule
Loading Bar Classes:
.wp-interactivity-router-loading-bar— Base styles.start-animation— Applied when navigation starts.finish-animation— Applied when navigation completes
Directive Processing Order
Directives are processed in this order on element entry:
data-wp-interactive— Establish namespacedata-wp-router-region— Setup routerdata-wp-context— Establish contextdata-wp-bind— Bind attributesdata-wp-class— Toggle classesdata-wp-style— Set stylesdata-wp-text— Set text contentdata-wp-each— Loop (processed last to avoid reprocessing)
On element exit, the order is reversed.
Incompatible Elements
Directives inside these elements are not processed:
<svg>— Not compatible with Tag Processor<math>— Not compatible with Tag Processor
A _doing_it_wrong notice is triggered if directives are found on these elements.
Namespace Resolution
When a directive doesn’t specify a namespace:
- Uses the namespace from the nearest ancestor
data-wp-interactive - Falls back to empty/undefined if no
data-wp-interactivefound
Explicit namespace:
<div data-wp-interactive="pluginA">
<span data-wp-text="pluginB::state.message">
<!-- Uses pluginB namespace explicitly -->
</span>
</div>