Repository navigation
Interactivity API: Add a data-wp-html directive #83076
Description
The Interactivity API has data-wp-text for updating text, but there's no equivalent for HTML. If a store value contains a description with links or other formatting, data-wp-text displays the markup as text instead of rendering it. This has come up in several issues, including #52904, rich descriptions inside data-wp-each (#70531), and server-rendered content fetched on demand (#81189).
Currently, developers need to update innerHTML manually, for example from a data-wp-watch callback. Those changes happen outside the Interactivity API's rendering process and may be overwritten during client-side navigation.
This proposal introduces a directive that handles HTML as part of the reactive updates, similar to how data-wp-text handles text. Since security is a concern, rendering HTML would require an explicit function call in the plugin's JavaScript. This prevents the insertion of arbitrary HTML by users who do not have permission to modify the code.
Proposal
Add data-wp-html and an asDangerousHTML() function exported from @wordpress/interactivity.
The directive would update an element's inner HTML using a value returned by asDangerousHTML(). Like other directives, its attribute would reference a store or context property, and the content would update when that value changes.
Plain strings wouldn't be accepted. Developers would call asDangerousHTML() from a derived-state getter or a store action to explicitly mark the content they want to render. For example:
import { asDangerousHTML, store } from '@wordpress/interactivity';
store( 'myPlugin', {
state: {
get description() {
return asDangerousHTML(
'<p>A <strong>formatted</strong> description.</p>'
);
},
},
} );<div data-wp-interactive="myPlugin">
<article data-wp-html="state.description">
<p>Loading description.</p>
</article>
</div>asDangerousHTML() wouldn't sanitize or modify the HTML. The developer is responsible for passing trusted content. The name makes that responsibility explicit, similar to React's dangerouslySetInnerHTML.
The returned value would be an immutable object that the directive recognizes. Adding HTML to data-wp-context or serializing it as JSON wouldn't mark it as trusted; that step would happen in the getter or action.
If the reference resolves to a plain string, null, or another unsupported value, the directive would leave the existing content in place. This also means a getter could return null while content is loading and keep the server-rendered fallback visible.
Use cases
Rich content inside data-wp-each
In #70531, each list item needs to display a description containing markup. The title could still use data-wp-text, while the description would use data-wp-html:
<ul data-wp-interactive="myPlugin/steps">
<template data-wp-each--step="state.steps">
<li>
<h3 data-wp-text="context.step.title"></h3>
<div data-wp-html="state.description"></div>
</li>
</template>
</ul>import { asDangerousHTML, getContext, store } from '@wordpress/interactivity';
const { state } = store( 'myPlugin/steps', {
state: {
steps: [ { id: 'setup', title: 'Set up your plugin' } ],
descriptionsById: {
setup: '<p>Start by <strong>activating</strong> the plugin.</p>',
},
get description() {
const { step } = getContext();
return asDangerousHTML( state.descriptionsById[ step.id ] );
},
},
} );Here, context identifies the current item, while the HTML comes from plugin-controlled state. The getter chooses the corresponding description and marks it for rendering, rather than trusting arbitrary HTML from context.
Server-rendered content fetched on demand
Another use case is fetching HTML from a plugin endpoint, as discussed in #81189. For example, opening a drawer could run an action that fetches its content and saves the response in state.html.
A derived-state getter would return null until the content is available, then return asDangerousHTML( state.html ). An element with data-wp-html="state.content" would display the response and update whenever the getter returns a new value. The plugin would be responsible for ensuring the endpoint returns content suitable for rendering.
This would cover displaying server-rendered HTML. Processing Interactivity API directives within that HTML would be a separate feature.
Scope
This proposal covers client-side rendering, with no PHP processor or editor integration. The server would leave the attribute untouched, and the element's server-rendered content would serve as a fallback before hydration, when JavaScript isn't available, or when using a version of the Interactivity API that doesn't support the directive.
Directives inside the inserted HTML wouldn't be processed, including after updates or client-side navigation. The content would still behave as regular HTML, so links and other native browser behavior would work. Supporting interactive fragments, as discussed in #81189, would need a separate proposal.
Support for sanitizing plain strings could be explored later using the same directive, while keeping asDangerousHTML() for explicitly trusted content. A PHP counterpart could also be considered separately. Neither is required for the initial client-side feature.
Activity
data-wp-htmldirective similar to data-wp-text for Interactivity API? #70531 as a duplicate of this issuenickchomey commentedon Sep 22, 2026
I can see how this would be useful for use with wp-each.
But you referenced #81189 a few times and I need to make it very clear that this proposal does NOT even slightly address what was proposed there. You even said
But that proposal already is such a separate proposal. The differences between this and that are far too great to warrant enumerating here, as they're already stated in detail there.
Moreover, that proposal is already implemented in full in #81462. I'd very much welcome and appreciate engagement with that proposal and PR, as it is functionality that is desperately needed in the IAPI and is not covered here.
I'd even suggest that #81462 be implemented instead of this one, or at least in conjunction with it, as it has vastly more functionality which can be used with all other aspects of the iAPI.
Interactivity API: Add a data-wp-html directive (#83092)