Custom Invoker Commands in React: cross-component communication without context or prop drilling? ⚡️
Native Custom Invoker Commands in React: command and onCommand examples, benefits and limitations.

The native Invoker Commands API (command and commandfor on <button>) is already rolling out actively across browsers. It lets you open dialogs or popovers declaratively right from HTML, without a single line of JS:
<button command="show-modal" commandfor="my-dialog">Открыть</button>
<dialog id="my-dialog">...</dialog>
But Sergio Xalambrí’s article explores a much more interesting scenario: Custom Invoker Commands.
How does it work in React?
Instead of built-in commands such as show-modal, we can pass a custom command (for example, command="--custom-action"). The browser itself sends a custom command event to the target element.
In React, we can attach a regular onCommand listener to the target DOM element:
function ActionButton({ targetId }: { targetId: string }) {
return (
<button command="--reset-counter" commandfor={targetId}>
Сбросить счётчик
</button>
);
}
function Counter({ id }: { id: string }) {
const [count, setCount] = useState(0);
return (
<div
id={id}
onCommand={(e) => {
if (e.command === "--reset-counter") {
setCount(0);
}
}}
>
<p>Счётчик: {count}</p>
</div>
);
}
The button and receiving element can live in completely different branches of the React tree. Passing the command requires neither global state, nor Context API, nor prop drilling.
➕ Benefits of the approach
- Decoupling: The action button knows absolutely nothing about the target component’s state. It only needs the DOM node’s
id. - Fewer rerenders: Unlike React Context or Redux/Zustand, clicking such a button does not trigger cascading rerenders of parent components or the context tree. It uses native browser event dispatching.
- Works before hydration (zero JS to send): Clicking the button generates a DOM-level event even while the page is loading. Once React hydrates the target component and attaches
onCommand, the handler runs. - Standardization: This is not a workaround or a React-specific pattern, but a native, specified web standard.
➖ Drawbacks and bottlenecks
- Tied to IDs:
commandforrequires a fixed link to a DOM element’sid. In React components with dynamicidvalues (for example, throughuseId()), you need to make sure identifiers are passed correctly. - TypeScript & React types: Because the specification is new, React types (
ButtonHTMLAttributes) and the JSXonCommandevent may require manual augmentation (declare module 'react') in some projects. - Strict syntax: Custom command names must start with two hyphens (
--my-command), or the browser will ignore the command or throw an error.
What do you think of using native browser events instead of React Context for simple UI commands?
I also recommend reading the article about implementing a Markdown toolbar in React using this approach: https://sergiodxa.com/tutorials/build-a-command-driven-markdown-toolbar-in-react