Last week a client with a new custom theme came to us. They had a few custom blocks plus a third-party plugin, all wanting dynamic UI behavior on the same content block. Sounds simple, right? Except the admin kept reporting odd JS errors, and the front-end was a mess: one interactive element would work, then break, then another would take over. It was a textbook case of WordPress Interactivity API conflicts, and it was a headache.
The problem, as anyone who’s wrestled with complex WordPress builds knows, is that HTML won’t let you put two attributes with the same name on one element. So if your custom block and, say, a third-party product configurator both try to attach a data-wp-on--click directive to the same button, only one wins. Usually not the one you want.
My first instinct, honestly, was to dig into the DOM and either wrap things in extra divs or use some JS to add and remove attributes based on context. That way lies a nightmare. You end up with brittle code, a bloated DOM, and more race conditions than you can shake a stick at. On top of that, you’re fighting the framework, which is never a good sign.
Solving WordPress Interactivity API conflicts with unique IDs
WordPress 6.9 adds a standardized way to handle this: unique IDs for Interactivity API directives. You can now put multiple directives of the same type on an element, as long as each one carries a different, unique ID. It’s clean, and it targets the conflict directly, which is exactly what my client needed. There are more details on this update on the WordPress Core blog.
<div
data-wp-watch---bbioon-my-custom-id="bbioonCallbacks.firstWatch"
data-wp-watch---bbioon-another-plugin-id="bbioonCallbacks.secondWatch"
></div>
See how bbioon-my-custom-id and bbioon-another-plugin-id tell the directives apart? Order matters too: the unique ID always comes last. The same pattern works with suffixes, like on the data-wp-on--click directive.
<button
data-wp-on--click---bbioon-plugin-a="bbioonPluginA::actions.someAction"
data-wp-on--click---bbioon-plugin-b="bbioonPluginB::actions.someAction"
>
Button example
</button>
What else is new: deprecations and TypeScript helpers
Beyond the directive ID fix, 6.9 tidies a few things up. The data-wp-ignore directive, meant to prevent hydration, is now deprecated. It caused more trouble with context inheritance and client-side navigation than it was worth, so good riddance. If you were using it, now’s the time to refactor.
For the TypeScript folks, WordPress 6.9 adds two helper types: AsyncAction<ReturnType> and TypeYield<T>. They handle the TypeScript issues that come up with asynchronous actions. If you build complex interactive components in TypeScript, they make things easier: you avoid circular type dependencies and can type your async operations explicitly. Solid typing saves a lot of debugging later.
So what’s the takeaway?
The Interactivity API keeps evolving, and these 6.9 changes make it steadier and easier to work with. The unique ID system removes a whole class of conflicts in complex setups, the deprecation clears out a feature that caused problems, and the TypeScript helpers bring it closer to modern tooling. Keeping up with these updates is worth it because it lets you build sites that stay stable instead of breaking every time something changes.
This stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.