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
- Ensure the submit button is a descendant of a `<form>` (or `<Form>`), or set `form="<formId>"` pointing at a form in the same document.
- If using `requestSubmit()`/`submit()` programmatically, pass the `<form>` element itself rather than the button.
- 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
- Always render submit buttons inside a `<form>`/`<Form>`.
- For split layouts, use the `form="id"` attribute to associate a button with a form.
- In React effects, confirm `form` is still mounted before calling submit.
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.