remix-run/react-router · error · Error

Cannot submit a <button> or <input type="submit"> without a

Error message

Cannot submit a <button> or <input type="submit"> without a <form>

What it means

`getFormSubmissionInfo` was given a `<button>` or `<input type=submit|image>` whose `.form` property is `null`. There is no associated form, so there is nothing to read the action/method/formData from and submission cannot proceed.

Source

Thrown at packages/react-router/lib/dom/dom.ts:300

  if (isFormElement(target)) {
    // When grabbing the action from the element, it will have had the basename
    // prefixed to ensure non-JS scenarios work, so strip it since we'll
    // re-prefix in the router
    let attr = target.getAttribute("action");
    action = attr ? stripBasename(attr, basename) : null;
    method = target.getAttribute("method") || defaultMethod;
    encType = getFormEncType(target.getAttribute("enctype")) || defaultEncType;

    formData = new FormData(target);
  } else if (
    isButtonElement(target) ||
    (isInputElement(target) &&
      (target.type === "submit" || target.type === "image"))
  ) {
    let form = target.form;

    if (form == null) {
      throw new Error(
        `Cannot submit a <button> or <input type="submit"> without a <form>`,
      );
    }

    // <button>/<input type="submit"> may override attributes of <form>

    // When grabbing the action from the element, it will have had the basename
    // prefixed to ensure non-JS scenarios work, so strip it since we'll
    // re-prefix in the router
    let attr = target.getAttribute("formaction") || form.getAttribute("action");
    action = attr ? stripBasename(attr, basename) : null;

    method =
      target.getAttribute("formmethod") ||
      form.getAttribute("method") ||
      defaultMethod;
    encType =
      getFormEncType(target.getAttribute("formenctype")) ||

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Ensure the submit button is a descendant of a `<form>` (or `<Form>`), or set `form="<formId>"` pointing at a form in the same document.
  2. If using `requestSubmit()`/`submit()` programmatically, pass the `<form>` element itself rather than the button.
  3. Verify the form is actually mounted (not unmounted by an early state change) at submit time.

Example fix

// before
return (
  <button type="submit">Save</button>
);

// after
return (
  <Form method="post">
    <button type="submit">Save</button>
  </Form>
);
Defensive patterns

Strategy: type-guard

Validate before calling

function hasForm(submitter: HTMLElement | null): submitter is HTMLButtonElement | HTMLInputElement {
  if (!submitter) return false;
  if (submitter instanceof HTMLFormElement) return true;
  return Boolean((submitter as HTMLButtonElement | HTMLInputElement).form);
}
if (!hasForm(submitter)) throw new Error('Submitter has no associated <form>');

Type guard

function isSubmitterWithForm(n: unknown): n is HTMLButtonElement | HTMLInputElement {
  return n instanceof HTMLButtonElement || (n instanceof HTMLInputElement && (n.type === 'submit' || n.type === 'image')) && n.form != null;
}

Prevention

When it happens

Trigger: Calling `submit(submitter)` (or `<Form>`'s implicit submission) on a submit button that has no enclosing `<form>` and no `form` attribute pointing at one; programmatically submitting a detached button node; a button whose `<form>` was removed from the DOM before the click handler ran.

Common situations: A `<button type="submit">` rendered outside any `<form>` (often a styling/layout accident); SSR markup that wraps the button in a form on the server but loses it on the client after hydration; React keying that re-parents the button away from its form.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/2d3ac51ade5bd0f5. Report an issue: GitHub.