For years, frontend development has had a slightly embarrassing relationship with HTML. We all read about semantic HTML, we talk about using the right landmarks, the right attributes, accessible forms, and meaningful elements. And then we go back to writing React.
While the whole industry is obsessed with AI, browsers are shipping features that make the platform even more capable. Features that let us remove JS, reduce state, and express UI behaviour in HTML. But since AI is trained in the past, these are ignored or not recommended enough through old patterns.
State boilerplate to open a dialog
Historically, in order to open a dialog or a popover, we need a chunk of custom boilerplate code.
<button id="open-dialog">Open dialog</button>
<dialog id="my-dialog">
<p>Dialog content</p>
<button id="close-dialog">Close</button>
</dialog>
<script>
const dialog = document.getElementById("my-dialog");
document.getElementById("open-dialog").addEventListener("click", () => {
dialog.showModal();
});
document.getElementById("close-dialog").addEventListener("click", () => {
dialog.close();
});
</script>
We need a button to open our dialog, the dialog itself, and then we write JS to explain the interaction to the browser: "When the user clicks the button, open the dialog".
If you're using React or Vue, this can get even more elaborate: You need some custom state, potentially pass it to your component, wire up an event handler, and then make sure everything stays in sync.
Whether vanilla or framework, the code above is completely reasonable on its own, every frontend developer has written something similar a hundred times. But after writing it for the thousandth time, it made me wonder: do we really need application state to represent that a dialog is open? The answer is: it depends. Sometimes state is needed. But sometimes we're just rebuilding behaviour that HTML and the browser can already provide for us out of the box in 2026.
Enters the chat: the Invoker Commands API
The Invoker Commands API provides us a way to declaratively assign behaviours to buttons, which then allows us to control these interactive elements. Instead of adding an event listener we can describe the relationship directly in HTML.
The attributes that help us are commandFor and command:
- commandFor: turns our button into a "command invoker". It takes the ID of the element to control as its value.
- command: Specifies the action to be performed on that element.
<button commandfor="mycoolpopover" command="toggle-popover">
Toggle the popover
</button>
<section id="mycoolpopover" popover>
<button commandfor="mycoolpopover" command="hide-popover">Close</button>
Awesome Popover content
</section>
How to do it in React?
All of these are transferable to your framework of choice:
export function DeleteButton() {
return (
<>
<button command="show-modal" commandFor="delete-dialog">
Delete account
</button>
<dialog id="delete-dialog">
<h2>Delete account?</h2>
<p>This cannot be undone.</p>
<button command="close" commandFor="delete-dialog">
Cancel
</button>
</dialog>
</>
);
}
The important detail here is that React's JSX property is commandFor, while the resulting HTML attribute is commandfor
Is that a really big deal?
At a first glance this looks like saving, maybe 10 lines of code. That's nice. But the most interesting is the shift of the responsibilities from us, back to the browsers.
The browser isn't becoming less capable because we're writing less JavaScript, but rather the opposite. We finally need less JS.
Browser support
Now, before everyone starts deleting their dialogs, there is a small catch: The Invoker Commands API is new. MDN currently lists it as Baseline 2025, with cross-browser availability in the latest browser versions since December 2025. Older browsers may not support it.
Top comments (22)
This is a really good reminder that the browser platform is evolving faster than a lot of our usual frontend patterns.
The part about LLMs reproducing older JS/React boilerplate is especially interesting. We often reach for state and event handlers before checking whether the platform already has a native solution.
command + commandfor looks like a small API change, but the bigger win is reducing unnecessary UI state and letting the browser own the interaction. Progressive enhancement with a feature-detected fallback also feels like the right approach while support continues to mature.
Definitely adding this to my list of APIs to experiment with. π
Good CSS/HTML coverage. The landscape here moves quickly β worth noting that container queries and :has() selector support have reached baseline, which can simplify many of the responsive patterns we previously needed JS for.
Yes this are cool additions, i want to probably write about them too! Too many things to get excited, too little time.
The struggle of having an overflowing backlog of exciting web platform features is incredibly real. If you do tackle the Invoker Commands API, narrowing the scope to something like custom dialog controls might save you some research time while still delivering high value to your readers. Feel free to reach out if you want to trade notes on the spec before you start drafting.
thanks you for the knowledge realy interesting
really appreciate a good human-written post! Just useful and straight to the point information!
Tell me about it! Back in the day, we had to attach an event listener to literally everything just to open a simple modal π
Modern HTML is making web dev so much smoother and less overwhelming for beginners. Loved this update
Nice reminder that browser-native features can replace a lot of UI state. Iβd pair this with feature detection and a graceful fallback, since support may vary across browsers.
Absolutely! I think the biggest benefit is reducing unnecessary JavaScript for interactions the browser can already handle. Itβs easy to fall back on familiar React patterns, so itβs refreshing to see native APIs getting more capable. Definitely something worth experimenting with. π
I seeβ¦! Even with coding, learning materials can be outdated, so you can end up learning outdated approaches too. This gave me a new perspective. Thank you!
thank you for great post
tbh html was always cool
i think you are right! people take it for granted!