https://developer.chrome.com/blog/command-and-commandfor
Skip to main content
Chrome for Developers
*
Get inspired Blog Docs
* Build with Chrome
* Learn how Chrome works, participate in origin trials, and build
with Chrome everywhere.
*
Web Platform
*
Capabilities
*
ChromeDriver
*
Extensions
*
Chrome Web Store
*
Chromium
*
Aurora
*
Web on Android
*
Origin trials
*
Release notes
* Productivity
* Create the best experience for your users with the web's best
tools.
*
DevTools
*
Lighthouse
*
Chrome UX Report
*
Accessibility
* Get things done quicker and neater, with our ready-made
libraries.
*
Workbox
*
Puppeteer
* Experience
* Design a beautiful and performant web with Chrome.
*
AI
*
Performance
*
CSS and UI
*
Identity
*
Payments
*
Privacy and security
* Resources
* More from Chrome and Google.
*
All documentation
*
Baseline
*
web.dev
*
PageSpeed Insights audit
*
The Privacy Sandbox
New in Chrome
[ ]
/
* English
* Deutsch
* Espanol - America Latina
* Francais
* Indonesia
* Italiano
* Nederlands
* Polski
* Portugues - Brasil
* Tieng Viet
* Turkce
* Russkii
* `bryt
* l`rbyW@
* frsy
* hiNdii
* baaNlaa
* phaasaaaithy
* Zhong Wen - Jian Ti
* Zhong Wen - Fan Ti
* Ri Ben Yu
* hangugeo
Sign in
* Blog
[ ]
Chrome for Developers
*
* Get inspired
* Blog
* Docs
+ More
* New in Chrome
* Build with Chrome
* Web Platform
* Capabilities
* ChromeDriver
* Extensions
* Chrome Web Store
* Chromium
* Aurora
* Web on Android
* Origin trials
* Release notes
* Productivity
* DevTools
* Lighthouse
* Chrome UX Report
* Accessibility
* Workbox
* Puppeteer
* Experience
* AI
* Performance
* CSS and UI
* Identity
* Payments
* Privacy and security
* Resources
* All documentation
* Baseline
* web.dev
* PageSpeed Insights audit
* The Privacy Sandbox
* Chrome for Developers
*
Blog
Introducing command and commandfor
Stay organized with collections Save and categorize content based on
your preferences.
Keith Cirkel
Keith Cirkel
Published: March 7, 2025
Buttons are essential to making dynamic web applications. Buttons
open menus, toggle actions, and submit forms. They provide the
foundation of interactivity on the web. Making buttons simple and
accessible can lead to some surprising challenges. Developers
building micro-frontends or component systems can encounter solutions
that become more complex than necessary. While frameworks help, the
web can do more here.
Chrome 135 introduces new capabilities for providing declarative
behaviour with the new command and commandfor attributes, enhancing
and replacing the popovertargetaction and popovertarget attributes.
These new attributes can be added to buttons, letting the browser
address some core issues around simplicity and accessibility, and
provide built-in common functionality.
Traditional patterns
Building button behaviours without a framework can pose some
interesting challenges as production code evolves. While HTML offers
onclick handlers to buttons, these are often disallowed outside of
demos or tutorials due to Content Security Policy (CSP) rules. While
these events are dispatched on button elements, buttons are usually
placed on a page to control other elements requiring code to control
two elements at once. You also need to ensure this interaction is
accessible to users of assistive technology. This often leads to code
looking a bit like this:
This approach can be a little brittle, and frameworks aim to improve
ergonomics. A common pattern with a framework like React might
involve mapping the click to a state change:
function MyMenu({ children }) {
const [isOpen, setIsOpen] = useState(false);
const open = useCallback(() => setIsOpen(true), []);
const handleToggle = useCallback((e) => {
// popovers have light dismiss which influences our state
setIsOpen(e.newState === 'open')
}, []);
const popoverRef = useRef(null);
useEffect(() => {
if (popoverRef.current) {
if (isOpen) {
popoverRef.current.showPopover();
} else {
popoverRef.current.hidePopover();
}
}
}, [popoverRef, isOpen]);
return (
<>
{children}
>
);
}
Many other frameworks also aim to provide similar ergonomics, for
example this might be written in AlpineJS as:
While writing this in Svelte might look something like:
Some design systems or libraries might go a step further, by
providing wrappers around button elements that encapsulate the state
changes. This abstracts state changes behind a trigger component,
trading a little flexibility for improved ergonomics:
import {MenuTrigger, MenuContent} from 'my-design-system';
function MyMenu({children}) {
return (
{children}
);
}
The command and commandfor pattern
With the command and commandfor attributes, buttons can now perform
actions on other elements declaratively, bringing the ergonomics of a
framework without sacrificing flexibility. The commandfor button
takes an ID--similar to the for attribute--while command accepts
built-in values, enabling a more portable and intuitive approach.
Example: An open menu button with command and commandfor
The following HTML sets up declarative relationships between the
button and the menu which lets the browser handle the logic and
accessibility for you. There's no need to manage aria-expanded or add
any additional JavaScript.
Comparing command and commandfor with popovertargetaction and
popovertarget
If you've used popover before, you might be familiar with the
popovertarget and popovertargetaction attributes. These work
similarly to commandfor and command respectively--except they're
specific to popovers. The command and commandfor attributes
completely replace these older attributes. The new attributes support
everything the older attributes did, as well as adding new
capabilities.
Built-in commands
The command attribute has a set of built-in behaviours which map to
various APIs for interactive elements:
* show-popover: Maps to el.showPopover().
* hide-popover: Maps to el.hidePopover().
* toggle-popover: Maps to el.togglePopover().
* show-modal: Maps to dialogEl.showModal().
* close: Maps to dialogEl.close().
These commands map to their JavaScript counterparts, while also
streamlining accessibility (such as providing the aria-details and
aria-expanded equivalent relations), focus management, and more.
Example: A confirmation dialog with command and commandfor
Clicking the Delete Record button will open the dialog as a modal,
while clicking the Close, Cancel, or Delete buttons will close the
dialog while also dispatching a "close" event on the dialog, which
has a returnValue property matching the button's value. This reduces
the need for JavaScript beyond a single event listener on the dialog
to determine what to do next:
dialog.addEventListener("close", (event) => {
if (event.target.returnValue == "cancel") {
console.log("cancel was clicked");
} else if (event.target.returnValue == "close") {
console.log("close was clicked");
} else if (event.target.returnValue == "delete") {
console.log("delete was clicked");
}
});
Custom commands
In addition to the built-in commands, you can define custom commands
using a -- prefix. Custom commands will dispatch a "command" event on
the target element (just like the built-in commands), but otherwise
will never perform any additional logic like the built-in values do.
This gives flexibility for building components that can react to
buttons in various ways without having to provide wrapper components,
traverse the DOM for the target element, or mapping button clicks to
state changes. This lets you provide an API within HTML for your
components:
Commands in the ShadowDOM
Given the commandfor attribute takes an ID, there are restrictions
around crossing the shadow DOM. In these cases you can use the
JavaScript API to set the .commandForElement property which can set
any element, across shadow roots:
Future proposals may provide a declarative way to share references
across shadow boundaries, such as the Reference Target Proposal.
What's next?
We'll be continuing to explore possibilities for new built-in
commands, to cover common functionality that websites use. Proposed
ideas are covered in the Open UI Proposal. Some of the ideas already
explored:
* Opening and closing elements.
* A "show-picker" command for and