Buttons and Submitting Forms
Prerequisites: Checkboxes, radios, selects, and text areas
The button element
<form action="/cart" method="post">
<input type="hidden" name="product" value="rye-loaf">
<label for="qty">Quantity</label>
<input id="qty" name="qty" type="number" min="1" value="1">
<button type="submit" name="action" value="add">Add to cart</button>
<button type="submit" name="action" value="save" formaction="/wishlist">Save for later</button>
<button type="reset">Reset</button>
</form>
button has three types:
submit— submits the form. This is the default inside a form.button— does nothing on its own; used with JavaScript.reset— resets every field to its initial value. Rarely helpful — it's easy to click by accident and lose everything.
A submit button's own name/value is sent with the form, so the
server can tell which button was pressed. formaction (and
formmethod, formnovalidate) override the form's settings for one
button.
Always set type on script buttons
<form action="/profile" method="post">
<label for="bio">Bio</label>
<textarea id="bio" name="bio"></textarea>
<button type="button" id="preview">Preview</button>
<button type="submit">Save</button>
</form>
Without type="button", clicking "Preview" would submit the form —
a classic bug.
Buttons vs links
- A link (
a href) goes somewhere — another page or place. - A button does something — submits, opens a menu, plays a video.
Keyboard users expect Enter to follow links and Space or
Enter to press buttons, and screen readers announce them
differently. Styling a link to look like a button is fine; using a
div or a link with href="#" as a button is not.
Buttons with icons
<button type="button" aria-label="Close">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 2 14 14M14 2 2 14" stroke="currentColor" stroke-width="2"/></svg>
</button>
An icon-only button needs an accessible name — here aria-label — or
screen readers just say "button".
What happens on submit
- The browser runs built-in validation (
required,type="email", ...) and stops with a message if something is wrong (Level 2 covers this in depth). - It collects every named, enabled control's value — skipping
unticked checkboxes and
disabledfields. - It sends a request to
actionwithmethod, and shows the page the server responds with.
disabled fields are skipped; use readonly for a field that can't be
edited but should still be submitted.
Practice
Why is this a problem, and how do you fix it?
<form action="/comments" method="post">
<textarea name="comment" aria-label="Comment"></textarea>
<button id="emoji">😀 Insert emoji</button>
<button>Post</button>
</form>
Show solution
Both buttons default to type="submit", so clicking the emoji button
posts the half-written comment. Add type="button" to it:
<form action="/comments" method="post">
<textarea name="comment" aria-label="Comment"></textarea>
<button type="button" id="emoji">😀 Insert emoji</button>
<button type="submit">Post</button>
</form>