Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

HTML has no built-in cancel button type. Choose the control by what Cancel should do: use <button type="button"> for a custom in-page action, an <a> link to leave for a known URL, and formmethod="dialog" to close a native dialog. Use reset behavior only when you specifically want to restore a form’s default values.

Start with the intended behavior

What Cancel should do Use
Submit the form <button type="submit">
Navigate to a known page <a href="/destination">
Run code or change in-page state <button type="button">
Restore controls to their HTML default values <button type="reset">
Close a native dialog form without sending it to a server formmethod="dialog"

Cancel and reset are not synonyms. A cancel action might leave the page, close a panel, or discard edits; HTML does not choose that behavior for you.

The safest custom Cancel button

<form id="profile-form" action="/profile" method="post">
  <!-- fields -->
  <button type="submit">Save changes</button>
  <button type="button" id="cancel-button">Cancel</button>
</form>

Specify the type on every button in a form. A button associated with a form whose type is missing or invalid normally acts as a submit button, so <button>Cancel</button> can submit the form. The HTML Standard defines submit, reset, and button behaviors; button has no built-in action. See the HTML Standard.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A type="button" control does nothing until you give it an action. It also does not initiate normal form submission or its constraint-validation process.

When Cancel means “go back to this page”

If the destination is a known URL, use a link. It navigates without JavaScript and does not submit the form:

<form action="/profile/edit" method="post">
  <!-- fields -->
  <button type="submit">Save</button>
  <a href="/profile">Cancel</a>
</form>

Style the link to look like a button if needed, but keep its link semantics: links navigate; buttons perform actions. Native links and buttons provide expected keyboard interaction and expose their roles to assistive technology. See W3C’s guidance on using HTML form controls and links.

If Cancel needs to check for unsaved changes or run other code before navigating, use a button instead:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<button type="button" id="cancel-button">Cancel</button>
<script>
  document.querySelector("#cancel-button").addEventListener("click", () => {
    window.location.assign("/profile");
  });
</script>

Use a destination that matches the product’s intended path. history.back() can send a user to an unrelated page—or be unhelpful if the form was opened directly or in a new tab. Prefer a known URL when that is where the user should go.

When Cancel means “discard my edits, but stay here”

For a static form, call reset() deliberately from a non-submit button:

<form id="settings-form">
  <label>Display name
    <input name="displayName" value="Taylor">
  </label>
  <button type="submit">Save</button>
  <button type="button" id="discard-button">Cancel</button>
</form>
<script>
  const form = document.querySelector("#settings-form");
  document.querySelector("#discard-button").addEventListener("click", () => {
    form.reset();
  });
</script>

HTMLFormElement.reset() restores form controls to their default values, as described by MDN. Those defaults are not necessarily the latest values your app fetched. For example, assigning input.value = profile.name changes the current value, but does not necessarily change the default value used by reset.

For API-loaded edit forms, capture the loaded record as the baseline and restore it explicitly. A simple example for one field:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const nameInput = document.querySelector("#name");
let originalName = "";

const profile = await fetch("/api/profile").then(response => response.json());
nameInput.value = profile.name;
originalName = profile.name;

document.querySelector("#cancel").addEventListener("click", () => {
  nameInput.value = originalName;
});

For a larger form, keep a structured snapshot of all relevant values and restore it through your app’s state-management approach. Account for checkboxes, radio groups, multi-selects, file inputs, and custom widgets; a single text-value assignment is not a general-purpose restore operation.

Why type="reset" is usually not the right Cancel

A reset button immediately restores controls to their initial/default values. It does not navigate, close a modal, restore data fetched later, or ask before clearing fields. MDN advises generally avoiding reset buttons because they can be activated accidentally and erase entered data; see its button reference and form tutorial.

If the real intention is to clear fields, label the control accordingly—for example, Clear all fields—rather than labeling a destructive reset as Cancel.

Cancel in a native <dialog>

For a form inside a native dialog, use the dialog form method for the control that closes it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<dialog id="edit-dialog">
  <form method="dialog" id="dialog-form">
    <label>Project name
      <input name="projectName" required>
    </label>
    <button value="cancel" formmethod="dialog">Cancel</button>
    <button value="save">Save</button>
  </form>
</dialog>
<script>
  const dialog = document.querySelector("#edit-dialog");
  dialog.addEventListener("close", () => {
    if (dialog.returnValue === "save") {
      // Read or process the form values here.
    }
  });
</script>

formmethod="dialog" closes the dialog without sending form data to a server. The activating button’s value is available as the dialog’s returnValue. See MDN’s dialog reference. Test with required fields left empty: Cancel should still close the dialog, while Save should remain subject to the intended validation. Don’t use a validation-bypass attribute as a substitute for choosing the dialog behavior.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Decide whether to confirm unsaved changes

Do not interrupt users with a confirmation when nothing has changed. For meaningful edits that would be hard to recover—especially long, complex, or high-value forms—warn before discarding, retain a draft, or offer another recovery path. W3C guidance discusses confirmation before irreversible actions and ways to return without unwanted data loss.

A basic dirty-state example:

let dirty = false;

form.addEventListener("input", () => {
  dirty = true;
});

cancelButton.addEventListener("click", () => {
  if (!dirty || window.confirm("Discard your unsaved changes?")) {
    window.location.assign("/dashboard");
  }
});

Real applications should track changes accurately: a user may edit a field and then put it back exactly as it was. Framework forms often provide a dirty/pristine state for this purpose. Keep the prompt specific and offer a way to stay and continue editing.

Accessibility and behavior checklist

  • Use a native <button> for an in-page action and a native <a> for navigation; don’t make a clickable div or span.
  • Give every form button an explicit type.
  • Use a visible label that matches the consequence, such as “Cancel,” “Discard changes,” or “Clear all fields.” Don’t rely on an icon alone.
  • Make sure keyboard users can reach and activate the control, and that closing a dialog or panel leaves focus in a sensible place.
  • Warn or provide recovery when cancellation could destroy substantial work. W3C’s guidance covers review, correction, and undo patterns for consequential form actions: form undo.

Common problems

  • Cancel submits the form: add type="button" for a custom action, or use a link for navigation. A missing type normally means submit for a form-associated button.
  • Cancel only clears or reverts fields: that is reset behavior, not navigation. Use a link or explicit navigation code if the user should leave.
  • Reset restores the wrong values: the form’s HTML defaults may differ from data loaded later. Save and restore an explicit baseline for the loaded record.
  • Required-field errors prevent cancellation: use type="button" for custom cancellation or formmethod="dialog" for a native dialog, not a normal form submission.
  • Back navigation goes somewhere unexpected: navigate to a known destination rather than blindly calling history.back().
  • A custom modal does not close: wire the button to the component’s close action; a plain type="button" has no automatic cancel behavior.
  • You need to cancel an already-submitted order or transaction: a form control cannot undo a server-side action. That requires a separate service-supported process and clear instructions about its availability and timing. See W3C guidance on post-submission cancellation procedures.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.