{"record":{"id":"2d3ac51ade5bd0f5","repo":"remix-run/react-router","slug":"cannot-submit-a-button-or-input-type-submit","errorCode":null,"errorMessage":"Cannot submit a <button> or <input type=\"submit\"> without a <form>","messagePattern":"Cannot submit a <button> or <input type=\"submit\"> without a <form>","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/dom/dom.ts","lineNumber":300,"sourceCode":"  if (isFormElement(target)) {\n    // When grabbing the action from the element, it will have had the basename\n    // prefixed to ensure non-JS scenarios work, so strip it since we'll\n    // re-prefix in the router\n    let attr = target.getAttribute(\"action\");\n    action = attr ? stripBasename(attr, basename) : null;\n    method = target.getAttribute(\"method\") || defaultMethod;\n    encType = getFormEncType(target.getAttribute(\"enctype\")) || defaultEncType;\n\n    formData = new FormData(target);\n  } else if (\n    isButtonElement(target) ||\n    (isInputElement(target) &&\n      (target.type === \"submit\" || target.type === \"image\"))\n  ) {\n    let form = target.form;\n\n    if (form == null) {\n      throw new Error(\n        `Cannot submit a <button> or <input type=\"submit\"> without a <form>`,\n      );\n    }\n\n    // <button>/<input type=\"submit\"> may override attributes of <form>\n\n    // When grabbing the action from the element, it will have had the basename\n    // prefixed to ensure non-JS scenarios work, so strip it since we'll\n    // re-prefix in the router\n    let attr = target.getAttribute(\"formaction\") || form.getAttribute(\"action\");\n    action = attr ? stripBasename(attr, basename) : null;\n\n    method =\n      target.getAttribute(\"formmethod\") ||\n      form.getAttribute(\"method\") ||\n      defaultMethod;\n    encType =\n      getFormEncType(target.getAttribute(\"formenctype\")) ||","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router/lib/dom/dom.ts#L282-L318","documentation":"The DOM interceptor in @react-router/lib/dom/dom.ts handles submit events for router-aware forms. When the submit target is a <button> or an <input type=\"submit|image\">, it reads target.form to inherit action/method/enctype from the associated form; if the control has no form (not nested in one and no valid form=\"id\" attribute), target.form is null and it throws immediately — you cannot build a submission without a form.","triggerScenarios":"A click/submit on <button type=\"submit\"> that is not inside a <form>; a control whose form attribute references a non-existent or detached form id; the wrapping form being removed from the DOM before the event fires.","commonSituations":"Component refactor moves a button out of its form; the form=\"some-id\" attribute has a typo'd or stale id; conditional rendering removes the <form> while keeping the button; portaling the button outside the form subtree.","solutions":["Wrap the control in a <form> — preferably react-router's <Form> or <form method=\"post\">","Point the control at an existing form via <button form=\"form-id\">","If the button isn't meant to submit anything, give it type=\"button\""],"exampleFix":"// before\n<button type=\"submit\">Go</button> // no <form> ancestor\n\n// after\nimport { Form } from \"react-router\";\n<Form method=\"post\">\n  <button type=\"submit\">Go</button>\n</Form>","handlingStrategy":"type-guard","validationCode":"// guard custom submit handling before reacting\ndocument.addEventListener(\"submit\", (e) => {\n  const t = e.target;\n  const associated =\n    t instanceof HTMLFormElement ||\n    ((t instanceof HTMLButtonElement || t instanceof HTMLInputElement) && t.form != null);\n  if (!associated) return; // ignore stray submit events\n});","typeGuard":"function isFormAssociatedSubmit(\n  target: EventTarget | null,\n): target is HTMLButtonElement | HTMLInputElement {\n  return (\n    (target instanceof HTMLButtonElement || target instanceof HTMLInputElement) &&\n    target.form != null\n  );\n}","tryCatchPattern":null,"preventionTips":["Always author submit controls inside <Form>/<form>","Prefer <button form=\"existing-id\"> over moving buttons out of forms","Use type=\"button\" for controls that only trigger JS handlers","Watch for portal/refactor changes that detach buttons from their form subtree"],"tags":["dom","forms","submit","event-handling","browser"],"backgroundTag":"button-outside-form","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}