Give buttons the action attribute, allowing them to make HTTP requests.
Proposal 2/3 in the Triptych Proposals.
<div>) lack
New attributes for the button:
action - the URL to request
method - the HTTP method to make the request with
When the action attribute is specified, clicking the button issues an HTTP request to the specified URL.
The method attribute can be used to change the HTTP method of the request; it defaults to GET if not specified.
When a button with the action attribute is included inside a form, it does not submit the form.
Instead, it makes the request without submitting additional data, exactly the same as if there had been no form at all.
One of the most common user interactions on the web is logging in or out of a website. A simple POST form, with username and password inputs, can model this interaction.
Logouts, however, do not submit data. There is currently no semantic way to represent a logout button; you have to wrap it in a form. This is one way to do it:
<form action=/logout method=POST> <button>Logout</button> </form>
With support for button actions, we can instead model this as a lone button creating or deleting a "session" resource.<button action=/logout method=POST>Logout</button>, which is also fine.
<button action=/session method=DELETE>Logout</button>This creates a simplifying symmetry for session management, and allows the logout action to take advantage of DELETE's idempotency.
sessions.js file, which exports two methods: post and delete. That same file can contain internal helper functions that handle authentication, nicely encapsulating the session management logic and re-using many of the same resources.
Social sites often let you endorse a piece of content, using a "like" or "upvote."
With button actions, this can be trivially implemented with just a URL.
<article> <button action="/articles/435/like" method="PUT">⇧</button> <span>The Triptych Proposals</span> </article>
After clicking the button, the server can return an identical page, except the PUT request has been replaced by a DELETE (to delete the upvote).
Many interactions on the web can be implemented with no request data, just a thoughtful URL structure and the right HTTP method. Web authors benefit tremendously from this pattern. Not only is it simpler, it's easier to audit, because almost all the relevant data can be found in the request logs.
Consider a simple job application webform, which uses button actions:
<form action="/apply" method="POST"> <input type="text" name="name"> <input type="email" name="email"> <textarea name="coverletter"></textarea> <button>Submit</button> <button formaction="/draft">Save Draft</button> <button action="/" method="GET">Cancel</button> </form>
The first part of the form is three inputs that the job seeker must fill out: a name, an email, and a cover letter.
The second part is three actions you can take:
you can submit the application, save a draft of it, or give up on applying entirely.
Each of these buttons interacts with the <form> element differently!
Submit button submits the form, sending a POST request to the /apply URL.
Save Draft button submits the form, but its formaction attribute changes the form's submission URL to /draft.
Cancel button doesn't interact with the form at all. It just navigates back to the home page.
Semantically, each of these buttons represents a reasonable form operation:
a user might choose to submit a form, save it as a draft, or cancel it entirely.
Without button actions, you can't describe the "Cancel" action correctly.formmethod and formaction methods, which modify the behavior of their containing form, cannot achieve the same "Cancel" experience: using them here would incorrectly navigate to the home page with the form's contents as a query parameter.
You could achieve the same behavior with a link, however, and just style it like a button.
That will be discussed further in the existing workarounds section.
Now let's say you save a draft and return to edit it. The web page looks like this:
<form action="/apply" method="POST">
<input type="text" name="name" value="Alexander Petros">
<input type="email" name="email" value="contact@example.com">
<textarea name="coverletter">
My name is Alex and my passion is enterprise software sales.
</textarea>
<button>Submit</button>
<button formaction="/draft/123" formmethod="PUT">Save Draft</button>
<button action="/draft/123" method="DELETE">Delete Draft</button>
<button action="/" method="GET">Cancel</button>
</form>
While the inputs of this form are the same, they have been pre-filled with the current values of the draft job application.Submit button still submits the form.
Save Draft button now sets the method to PUT and the URL to /draft/123, which references the specific draft being modified.
Delete Draft button uses button actions to issue a DELETE request to /draft/123, with no body.
Cancel button still navigates to the home page.
Once again, all of these buttons are, from the user's perspective, co-equal form controls. But only two of the buttons rely on the form data; two of them do not. Adding buttons that can perform requests independently not only removes the need to wrap them in forms, it also allows for simpler nesting within forms to achieve a complete set of form controls.
To make a request with a button, the action attribute must be specified with a valid URL.
The method attribute defaults to GET if not specified;
it can also be set to all supported HTTP form methods.
Buttons can issue requests if they are type=submit, the default button type.submit is a mildly regrettable name in this context, as it is not typically used outside the context of a form (one does not really "submit" a link navigation).
It is far more important, however, that the button actions work with the default type, which is and always will be submit, than it is to have a better name for that functionality.
Besides, it's far from the most confusing default name in HTML.
type=submit (or no type specified) is capable of making requests.
Buttons that have any other type attribute should ignore the method and action attributes.
Buttons with the action attribute that are children of a form will not submit any of the form data when clicked.
If the method attribute is specified but no action attribute is, then the method attribute is ignored;
the existing formmethod attribute should be used to alter the method of the form.
Button actions are subject to CORS. All cross-origin requests require a preflight and there are no so-called "simple" cross-origin requests for partial page replacements. Same-origin requests proceed as normal.
This can be accomplished without changes to fetch spec, by adding a non-safelisted header to the request.
The most common question this proposal elicits is "why not just use a link?"
The answer is that links are simply the wrong element for certain user experiences.
Buttons and links have different meanings to the user and different behavior in the browser.
This section explains why those situations occur and how this proposal closes a small but extremely common gap in their capabilities—one that has led to decades of incorrect, insecure, and inaccessible websites.
The W3C's ARIA Authoring Practice Guide (APG) is an authoritative reference for the semantics of HTML elements. It defines a link:
A link widget provides an interactive reference to a resource. The target resource can be either external or local, i.e., either outside or within the current page or application.
And a button:
A button is a widget that enables users to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation.
Links represent a place you can go; buttons represent an action you can take. The behavioral differences that arise from these semantics are so baked into our experience of using a web browser that we often don't consciously notice them.
Consider, for instance, the many affordances that browsers offer for opening a link in a new browsing context.In a hypermedia context, affordances are typically discussed in terms of
the mechanisms by which the hypermedia user and the hypermedia client
interact with one another to achieve the goals of the hypermedia user.
In the context of this paragraph, middle-clicking to open a new tab is an affordance; it exists because the authorial intent of a hyperlink is very precise, and the browser can build useful features based on that.
target attribute on the link, but this is much closer to a default than a mandate.
Users can still copy the link and open it in the context of their choosing.
Buttons, however, have none of this functionality.
By default, they cannot be middle-clicked, right-click copied, or hovered over for more information.
Clicking a button with a modifier key pressed is exactly the same as clicking without one.
Their context menus offer no sharing functionality.
These are not omissions, but deliberate choices that reflect the button's semantics:
a button triggers actions and events inside a fixed browser context.target="_blank", or the parent context, with target="_parent".
And, of course, the target attribute can be employed to effect changes on an iFrame (used to brilliant effect by htmz), which might be understood by the user as the current context, but is actually a child context.
Therefore, we use the more precise language, even though it's important to call out that the users are overwhelmingingly likely to understand buttons as impacting the "current" context.
The lack of customization afforded by the button illustrates how it serves a complimentary purpose to a link. A link always represents the same action—navigate to a URL—but the user has many options for where that action will take place. A button can represent almost any action—navigate to a URL, open a dialog, delete an item—but the user is not allowed to choose where it takes place.
Notice how "navigate to a URL" is included as a possible result of a clicking a button.
Both links and buttons are used in situations where they perform navigations.
| Navigation Action | Non-Navigation Action | |
|---|---|---|
| Link | Go to a new page | Scroll to an element |
| Button | Submit a form | Open a dialog |
All four of these actions are consistent with the semantics we previously established. Both uses of the link bring the user to a destination and both uses of the button take an action within their existing context. Navigation is completely orthogonal to whether an interactive element should be a link or a button.
Not only can buttons trigger full-page navigations, they can do so with both GET and POST requests.
Most web users are unfamiliar with HTTP method semantics, but they are accustomed to the idea that clicking a button may or may not result in a "modifying" action.
If buttons represent an action the user can take, why would the web author want to be able to implement that action with a page navigation?
First, it needs to be re-emphasized that triggering a page navigation is already a fundamental feature of the button.
When you click the login button on a website, you execute a page navigation.
What differentiates buttons is that they represent an action that is not available to be re-contextualized by the user;
browsers honor this by giving buttons a feature set that doesn't allow for re-contextualization.
The need for a button that can execute a clean GET navigation (i.e. no query parameter) arises because navigation is the most powerful, most reliable, and most user-friendly primitive that web authors have to change the DOM.
The web author community has conclusively aligned on the need for a button that can navigate.
The official designs systems of the United States and the United Kingdom, which are extensively researched and tested, both contain affordances for adding a "button" class that styles a link to look like a button.usa-button and govuk-button, respectively.
Each of these buttons indicates a way for the user to modify the current application context. Not only is page navigation the simplest way to implement that, it's also often the recommended one: the UK Design System's Accessibility Guidelines explicitly encourages a web experience that can work without JavaScript.
Of these four examples, "Sign in" and "Save and continue" can both be implemented with HTML, because they would use a form to submit data.
"Start now" and "Continue", however, are both buttons that might be implemented as basic page navigations.
With a mandate to support progressive enhancement, HTML buttons can't be used for those, since they don't support basic navigation.govuk-button class.
Naturally, the other examples all just use the HTML button tag instead.
This practice is often met with an admonishment: buttons should look like buttons and links should look like links. That's true, but it assumes that web authors do this out of ignorance, which is not (always) correct. Making links look like buttons is much more work than using a button; authors do it because buttons lack functionality so important that they are compelled to implement it themselves.
Styling links as buttons is fundamentally inaccessible, but it is the official recommendation of designs systems that prioritize accessibility because it's the only way to implement progressive enhancement.
The MDN page for the ARIA button role contains the following callout:
Warning: Be careful when marking up links with the button role. Buttons are expected to be triggered using theSpaceorEnterkey, while links are expected to be triggered using theEnterkey. In other words, when links are used to behave like buttons, addingrole="button"alone is not sufficient. It will also be necessary to add a key event handler that listens for theSpacekey in order to be consistent with native buttons.
Both the US and UK design systems heed the MDN warning and use JavaScript to implement spacebar selection, but spacebar selection is just one of the many ways that buttons behave differently from links.
When web authors style links to look like buttons, they inherit the responsibility to make links behave like buttons in all possible contexts. This is an impossible task. You can intercept middle-clicks with an event handler, but you can't change the system context menu, nor can you override a user's preference for alternative styling.
Web authors face a catch-22: if you need a button to perform a simple navigation in the current context, and you don't want to reply on scripting, you have to use a link, but using a link where you actually need a button introduces accessibility concerns that can only be mitigated with scripting. Even with the best mitigations, using a link button will still misrepresent and mislead the user in contexts you can neither anticipate nor control.
It's good that the browser don't let web pages override user preferences, because that allows them to provide a consistent and secure experience on the web.
If, instead, web authors could simply write <button action="/start">, all these problems would disappear and web authors could easily leverage the excellent user experience that web browsers have worked so hard to build.
Each of the Triptych Proposals stands on its own, but it's worth mentioning that the addition of Partial Page Replacement adds another use for the button that reinforces its existing semantics.
Buttons can trigger many possible effects on the page that aren't navigation, like opening and closing dialogs.
Partial page replacement, made available to buttons, would be another non-navigation effect.
With a CSS selector provided to the target attribute, activating the button would trigger a network request and change a part of the DOM based on that response.
Adding partial page replacement as a target for the button's action would encourage proper use of buttons by providing a feature exclusive to them. Links would continue to represent destinations that can be independently navigated, while buttons would be able to trigger additional HTTP requests that only make sense within the context of an existing document.
One does not have to look far to find websites that use button-like styling to emphasize their most important links.
These are sometimes called a Call To Action (CTA).
For instance, the ARIA APG homepage has many CTAs that look like flat buttons.
This proposal does not take a stance on whether it is okay to style your links this way.<a> and <button> have different meanings to the browser and that there are situations in which using a button to perform a fixed-context navigation is unambiguously correct, which is why HTML should support them.
Despite looking like buttons, CTAs are not one of those situations.
We have no evidence that lack of support for button actions causes web authors and users to blur the lines between links and buttons, but it would be much easier to promote consistent guidance that "links should always look like links" if there weren't any valid use cases for links that look like buttons.
The
For buttons that would issue GET requests, it is possible to instead use links and style them like a button. This has a couple key problems.
The first is that people often incorrectly use it for unsafe actions.
The most famous example of this is the persistent scourge of logout links, which break the entire experience when the browser pre-fetches the link and accidentally logs the user out.
<a href=/logout class=button>Logout</a>
The root cause of this mistake is that links issue GET requests to the href URL, but logging out is an unsafe request, which should use a POST request.
The recommended solution with current HTML is to create a logout button, wrapped in a form that makes a POST request:
<form action=/logout method=POST> <button>Logout</button> </form>
It's not surprising that people continue to make the logout link mistake, because the recommended solution is more verbose and looks wrong. Why is there a form element when no form data is being submitted? With this proposal, we can have a solution that both looks and is correct.
<button method="POST" action="/logout">Logout</button>
Creating a valid solution that is as simple as a hyperlink will not immediately make people stop using hyperlinks incorrectly, but it will resolve the fact that the platform implicitly discourages their correct use today.
"Link buttons" for safe requests avoid the HTTP semantics problems, but they still have the HTML semantics problems described in
As noted above, the recommended way to make buttons perform navigations in HTML is to wrap them in a form.
<form action=/logout method=POST> <button>Logout</button> </form>
Unfortunately, this only works for POST requests. If you do this with a GET request...
<form action=/cancel method=GET> <button>Cancel</button> </form>
... it results in a request to /cancel?, with an extraneous query parameter, which is incorrect.? at the end of your URLs is not a big deal, because severs can "just" ignore the query parameter.
This is a faulty assumption.
Even though example.com/cancel? and example.com/cancel have the same path, they are not the same URL—one has a query and one doesn't.
While many sophisticated routers parse queries into separate objects, this would inhibit the use of HTML for simple server projects and prototypes—exactly where the batteries-included functionality of HTML is most needed.
As noted many times in this proposal, mechanisms that look wrong—and are wrong—go unused by the web author community.
This is especially true when the kludge is visible to the user, as it would be in the URL.
No authorities recommend using GET forms as a substitute for button links, even with all the
The form wrapper works for POST requests, but it's still semantically incorrect: there's no form to fill out.
This technique also cannot be used within forms, for any method.
Consider how you might implement /draft/123/delete.
<form action="/apply" method="POST">
<input type="text" name="name" value="Alexander Petros">
<input type="email" name="email" value="contact@example.com">
<textarea name="coverletter">
My name is Alex and my passion is enterprise software sales.
</textarea>
<button>Submit</button>
<button formaction="/draft/123" formmethod="PUT">Save Draft</button>
<button formmethod="DELETE">Delete Draft<button>
<a href="/">Cancel</a>
</form>
Here the formmethod attribute has been set to DELETE so the browser (assuming DELETE is supported) will issue a DELETE request to /apply.
Unfortunately, this request will include all of the values in the form, which is neither expected nor desired.
This limitation only exists in HTML, not the web platform itself. Developers often eschew HTML functionality entirely by describing the UI with buttons and adding the appropriate functionality with scripting. Button actions remove this limitation, allowing HTML to model these form actions both semantically and functionally.
The HTML standards body recommends that authors model additional actions inside HTML forms by creating additional empty forms elsewhere, then using the form attribute on the button to refer to them.Delete Draft button that lives below the parent form, because HTML forms cannot be nested.
<form action="/apply" method="POST">
<input type="text" name="name" value="Alexander Petros">
<input type="email" name="email" value="contact@example.com">
<textarea name="coverletter">
My name is Alex and my passion is enterprise software sales.
</textarea>
<button>Submit</button>
<button formaction="/draft/123" formmethod="PUT">Save Draft</button>
<button form="delete-form">Delete Draft<button>
<a href="/">Cancel</a>
</form>
<form id="delete-form" action="/apply" method="DELETE"></form>
This works, but it is a lot more difficult to follow, due to loss of locality.
Extensive use of this pattern creates the conditions for a surprising XSS vulnerability. If an attacker can inject a form elsewhere in the page, it can control the action of that button.
<form id="delete-form" action="https://evil-url.example.com" method="POST"></form>
This XSS vulnerability is especially dangerous because it allows an attacker to alter a trusted part of the page, even without scripting enabled. Many of the traditional defense-in-depth mechanisms, which focus on limiting sources of scripting, would not catch this.
With the introduction of button actions, HTML can encourage a simpler, more secure pattern:
<button action="/apply" method="DELETE">Delete Draft<button>
A web service with the same security policies, but using a button action, would not be vulnerable to having one of its page controls hijacked by an un-sanitized form, because its behavior is defined entirely within itself.
Critical mistakes happen when an implementation is too complex to audit or understand. By creating a blessed pattern where buttons can properly define their own functionality, HTML can encourage more secure development patterns with less possibility for error.
Finally, if this solution is sufficient, why is it vanishingly rare?
name and value attributes.<button action=/members method=POST name=person value=Alex >Signup </button>
This is a reasonable thing to add, and would fit well within the existing semantics of the button, which can have values that get submitted along with their parent form.
It is left out of the main proposal, however, in order to curtail scope. Most of the use-cases for this can also be accomplished with URLs and query parameters.
Michael West proposed a workaround where buttons would get a _self keyword to use with the form attribute.
This proposal neatly sidesteps sanitization questions by not introducing any new elements.
<button form="_self" formaction="/logout" formmethod="post">Logout</button>
Unfortunately, this would not achieve the goals of the proposal. Quite simply, people would not use it.
To start, _self forms still have the query parameter problem.
Even in the cases where it does work, the semantics are still confusing.
This proposal aims to simplify using buttons outside the context of a form;
requiring that that buttons use three form* attributes to take non-form actions is confusing and counterproductive.
Ostensibly, this alternative allows for an elegant HTML-only polyfill, using with a single dangling form:
<form id="_self"></form>
The simplicity of the polyfill also demonstrates why this alternative doesn't accomplish anything.
Authors who want to write HTML this way can already include a dangling form at the top of all their documents, but the HTML you end up writing is just as cumbersome as the existing workarounds;
if it wasn't, experts would recommend this pattern today.<form id="_self"><form> to the top of your document and write all buttons this way, are you going to?
Can you imagine writing a beginner's guide to HTML that recommends this practice?
Another goal, building a simple metaphor for partial page replacement, becomes prohibitively verbose if you run it entirely through the form semantics.
<button form="_self" formaction="/next" formmethod="get" formtarget="#articles">Logout</button>
Authors reach for alternative solutions when those solutions are more expressive than what the platform provides. HTML is an extremely expressive language—links just happen to be much more expressive than buttons in situations where buttons should be used. An enhancement which doesn't correct this imbalance does not meet the goals of this proposal: to introduce syntax that authors actually want to use.
The action attribute was chosen to conform with existing HTML grammar, but lots of names would work.href, url, dest, and location.
<button location="/logout" method="post">Logout</button>
The choice of a new name succeeds where the form* methods fail because they create a syntax that plainly states what the button does, without semantic kludges that push the user away.
This is something that would be easy to teach people how to use instead of a link.
This would resolve a concern raised by the standards committee that using action on buttons might have web compatibility problems.action to the button is incompatible with.
This would assist us in designing a version of this proposal that doesn't have compatibility concerns!
formaction was originally supposed to be called action, and the name was changed to formaction for this reason.formaction method alters the action of its parent form.
The name action makes more sense when it refers to its own element's action, just like it does with the form, and, if this proposal were to be adopted, the button.