skip to content

An HTML form has two submit buttons, Save and Publish. How does the server know which one the user pressed, and how can one button post to a different URL than the form's action?

level: middleimportance: should knowfreq 46%

answer

  1. only the pressed button is serialized
  2. give each button a name and a value
  3. form-prefixed attributes override per button
  4. Enter uses the first submit button
  5. pass the submitter to FormData

basics

~20 s

Only the activated submit button contributes its name and value to the submission, so giving each button a name and a distinct value tells the server which was pressed. A button's formaction attribute overrides the form's action URL for that button only.

solid answer

~50 s

Buttons are not serialized as a group: exactly one — the **submitter** — contributes an entry, and it is the button the user activated. So give both `name="intent"` with values `save` and `publish`, and the payload carries `intent=publish` only when Publish was pressed. A button with no `name` contributes nothing, which is why forms with one button usually leave it off. Each submit button can also override the form for its own activation: `formaction` swaps the target URL, `formmethod` the HTTP method, `formenctype` the encoding, `formtarget` the browsing context. From script, the `submit` event is a `SubmitEvent` whose `submitter` property is the element that triggered it — useful because building `new FormData(form)` without passing the submitter omits the button's entry. When the user submits implicitly by pressing Enter, the submitter is the form's default button: the first submit button in tree order.

go deeper

for a junior

Know that a <button> inside a form submits by default, and that giving each submit button a name and a distinct value is how the server tells them apart.

for a middle

Explain the submitter concept: exactly one button contributes an entry, the form-prefixed attributes override action, method, enctype and target for that button, and Enter uses the first submit button.

for a senior

Show the script-side consequences — reading SubmitEvent.submitter, passing it into FormData, and not disabling a named submit button before the entry list is built — and order buttons so the default is the safe one.

for a principal

Decide the codebase convention: one endpoint with an intent field, or distinct endpoints via formaction, and make sure destructive operations are never reachable by an accidental default submission.

## One submitter, one entry When a form is submitted, the browser records which element triggered it. That element is the **submitter**, and it is the only button whose `name`/`value` pair enters the entry list. Every other button in the form — other submit buttons, `type="button"`, `type="reset"` — contributes nothing. This is the whole mechanism behind multi-action forms: ```html <form action="/posts/42" method="post"> <textarea name="body"></textarea> <button name="intent" value="save">Save draft</button> <button name="intent" value="publish">Publish</button> </form> ``` Pressing Publish submits `body=…&intent=publish`; pressing Save submits `intent=save`. A single server endpoint branches on one field. Note that a `<button>` with no `type` attribute already defaults to `type="submit"`, so both of these submit without saying so. Two details worth internalising. First, a button with no `name` never appears in the payload no matter how it was pressed — that is the normal case for a one-button form. Second, `<button name="x" value="y">Label</button>` submits `y`, not the visible label; with `<input type="submit">` the `value` attribute *is* the label, which is one reason `<button>` is generally the better element. ## Per-button overrides A submit button can override four aspects of the form, for its own activation only: - `formaction` — a different URL than the form's `action`. - `formmethod` — a different method, e.g. one GET button on a POST form. - `formenctype` — a different body encoding. - `formtarget` — a different browsing context for the result. ```html <form action="/posts/42" method="post"> <textarea name="body"></textarea> <button>Save</button> <button formaction="/posts/42/preview" formtarget="_blank">Preview</button> </form> ``` Preview posts the same entries to a different endpoint in a new context, with no scripting involved. Which style you choose is a design call: `name`/`value` keeps one endpoint and one place to branch, while `formaction` keeps the routes distinct and self-describing. Mixing both in one form is usually a smell. ## Reading the submitter from script The `submit` event is a `SubmitEvent`, and its `submitter` property is the element that triggered submission — the button, or the default button when the user pressed Enter: ```js form.addEventListener("submit", (event) => { event.preventDefault(); const data = new FormData(form, event.submitter); console.log(data.get("intent")); }); ``` The second argument matters. `new FormData(form)` alone builds the entry list *without* any submitter, so the button's `name`/`value` is absent — a genuinely confusing bug when a script-driven submission suddenly stops carrying `intent` that the native submission carried fine. Passing `event.submitter` reproduces the browser's own list. If you need a fallback, `document.activeElement` at submit time is not reliable, so prefer the property; where it is unavailable you can record the last clicked button yourself. ## Implicit submission picks the default button When the user presses Enter inside a text field rather than clicking, the browser submits using the form's **default button**: the first submit button in tree order. In the two-button example above, Enter therefore means Save, not Publish. That is a real design consideration — order the buttons so the safest action comes first in the DOM, and never place a destructive submit button first just because the visual design puts it on the left. If ordering fights the layout, the styling layer can reorder the presentation while the DOM keeps the safe default. ## Related traps - A destructive action such as Delete is usually better as its own form or its own endpoint via `formaction`, so that an accidental Enter cannot reach it. - Adding a click handler to a submit button that also lets the form submit natively gives you two code paths for one user action; either make the button `type="button"` and do the work in the handler, or handle the form's `submit` event and read `submitter`. - Disabling the submit button on click to prevent double submission removes it from the entry list if it has a `name`; disable it *after* the submission has been dispatched, or use a hidden field to carry the intent.

  • A script builds new FormData(form) in the submit handler and the intent field is missing, though native submission included it. Why?
    Because the submitter is not part of the form's controls in the ordinary sense — its entry is added only because it triggered the submission. `new FormData(form)` has no idea which button that was, so the pair is omitted. Pass it explicitly as the second argument, `new FormData(form, event.submitter)`, and the entry list matches what the browser would have sent.
  • Two submit buttons are Delete and Save, with Delete first in the markup. What is the risk?
    Implicit submission uses the form's default button — the first submit button in tree order — so pressing Enter in any text field triggers Delete. Put the safe action first in the DOM and let styling handle visual order, or move the destructive action into its own form or its own endpoint via `formaction` so a stray Enter cannot reach it.
  • When would you choose formaction over a name/value pair on the buttons?
    Use `formaction` when the two actions are genuinely different resources — preview versus publish — so the routes stay self-describing and each endpoint has one job. Use `name`/`value` when it is one operation with a mode flag, keeping a single endpoint that branches on a field. Doing both in one form usually means the intent has not been decided.

saying these in an interview costs you the question

  • Thinks every submit button in the form is serialized
  • Expects a button's visible label to be the submitted value
  • Assumes new FormData(form) includes the pressed button
  • Believes Enter activates whichever button looks primary
  • Uses action instead of formaction on a button

context