All About That Button, âBout That Button
In modern SPAs itâs common to immediately escape baked-in browser behaviors. For example, using <button>
often looks like this:
<div>
<input type="text" name="q" />
<button
type="submit"
onClick={(e) => {
// Stop the baked-in behavior
e.preventDefault();
// Do something with the input's value
}}
>
Search
</button>
</div>
But a framework like Remix encourages writing mutations as declarative HTML that works without â or, perhaps better stated, before â JavaScript, using semantic elements like <form>
and <button type="submit">
.
<form action="/search">
<input type="text" name="q" />
<button type="submit">Search</button>
</form>
From this starting point of HTML â which functions before JavaScript loads & executes â you can then begin to progressively enhance your <form>
with JavaScript that intercepts default browser behavior (e.g. <form onSubmit={...}>
) and enhances the experience however you prefer.
As Iâve worked more closely with forms and buttons, Iâve learned a few things.
For example, did you know you can submit a form with a button that lives outside of the form it submits? Use the form
attribute:
<form id="my-search-form" action="/search">
<input type="text" name="q" />
</form>
<!-- Somewhere else in the DOM -->
<button type="submit" form="my-search-form">
Search!
</button>
Or, when a form submits you can open the result in a new tab (you can stick target
on the <form>
itself too and itâll do the same thing):
<form action="https://google.com/">
<input type="text" name="q" />
<input type="hidden" name="site" value="my-blog.com" />
<button type="submit" formtarget="_blank">Search</button>
</form>
Thatâs a neat progressive enhancement trick because it allows the user to input a query right there on your website and then, if JavaScript is enabled/working, you e.preventDefault()
and take over the interaction there on the page. But if JS is disabled or fails to load, the interaction still works and submitting the form opens a new tab on the userâs device with results for their query.
Thereâs a bunch of other button attributes for overriding parent form behaviours, such as: formmethod
, formenctype
, formaction
, and formnovalidate
.
If youâve worked in a Remix app where youâre trying to build user interactions that work both with and without (or before and after) JavaScript, youâve likely encountered many of these. They are very useful mechanisms.
âBut whyâ, you might ask, as an example, âwould you want to have two buttons on a form, one that traditionally submits the form with validation and one that uses formnovalidate
to submit the form and bypass validation?â
I could go into detail describing one such use-case in a recent codebase, but it will suffice to rather quote the imitable Chris Coyier who had a similar issue years ago:
When you submit [
<form action="/submit">
], itâs going to go to the URL/submit
. Say you need another submit button that submits to a different URL. It doesnât matter why. There is always a reason for things. The web is a big place
A big place indeed.