{"record":{"id":"bbf5177fd1b19b55","repo":"remix-run/react-router","slug":"cannot-submit-element-that-is-not-form-button","errorCode":null,"errorMessage":"Cannot submit element that is not <form>, <button>, or <input type=\"submit|image\">","messagePattern":"Cannot submit element that is not <form>, <button>, or <input type=\"submit\\|image\">","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/dom/dom.ts","lineNumber":340,"sourceCode":"    // Build a FormData object populated from a form and submitter\n    formData = new FormData(form, target);\n\n    // If this browser doesn't support the `FormData(el, submitter)` format,\n    // then tack on the submitter value at the end.  This is a lightweight\n    // solution that is not 100% spec compliant.  For complete support in older\n    // browsers, consider using the `formdata-submitter-polyfill` package\n    if (!isFormDataSubmitterSupported()) {\n      let { name, type, value } = target;\n      if (type === \"image\") {\n        let prefix = name ? `${name}.` : \"\";\n        formData.append(`${prefix}x`, \"0\");\n        formData.append(`${prefix}y`, \"0\");\n      } else if (name) {\n        formData.append(name, value);\n      }\n    }\n  } else if (isHtmlElement(target)) {\n    throw new Error(\n      `Cannot submit element that is not <form>, <button>, or ` +\n        `<input type=\"submit|image\">`,\n    );\n  } else {\n    method = defaultMethod;\n    action = null;\n    encType = defaultEncType;\n    body = target;\n  }\n\n  // Send body for <Form encType=\"text/plain\" so we encode it into text\n  if (formData && encType === \"text/plain\") {\n    body = formData;\n    formData = undefined;\n  }\n\n  return { action, method: method.toLowerCase(), encType, formData, body };\n}","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router/lib/dom/dom.ts#L322-L358","documentation":"React Router's submit path (useSubmit, useFetcher().submit, router.submit) extracts method/action/formData from a DOM target, but only <form>, <button>, and <input type=\"submit|image\"> carry submission semantics. getFormSubmissionInfo() in lib/dom/dom.ts:339 throws this error when it receives any other HTMLElement. The library refuses to guess an action or method for elements that have none.","triggerScenarios":"Calling submit(e.currentTarget) from a <select onChange={...}> (the currentTarget is the <select>, not the form); passing an <a>, <div>, or <input type=\"text\"> to submit(); calling router.submit(document.querySelector('#wrapper')) where the node is a non-submittable element; submitting a ref that points to a container div instead of the <Form>.","commonSituations":"Filter/search dropdowns that auto-submit a form on change; migrating native form code where developers submit the changed control instead of the form; refs attached to wrapper elements around the form.","solutions":["Submit the owning form instead of the control: submit(event.currentTarget.form)","Or submit data directly instead of an element: submit({ q: value }, { method: 'get' })","If using a button, make sure it is a real <button type=\"submit\"> inside the <Form>","Double-check that the ref/value you pass resolves to the <form> node itself"],"exampleFix":"// before\n<select onChange={(e) => submit(e.currentTarget)} />\n// after\n<select onChange={(e) => submit(e.currentTarget.form)} />","handlingStrategy":"type-guard","validationCode":"const el = e.currentTarget;\nif (el instanceof HTMLFormElement || el instanceof HTMLButtonElement) {\n  submit(el);\n} else if (el instanceof HTMLInputElement && (el.type === 'submit' || el.type === 'image')) {\n  submit(el);\n} else if (el instanceof HTMLElement && el.form) {\n  submit(el.form);\n} else {\n  submit(new FormData(el.form ?? undefined));\n}","typeGuard":"function isSubmittable(el: EventTarget | null): el is HTMLFormElement | HTMLButtonElement | HTMLInputElement {\n  if (!(el instanceof HTMLElement)) return false;\n  return (\n    el instanceof HTMLFormElement ||\n    el instanceof HTMLButtonElement ||\n    (el instanceof HTMLInputElement && (el.type === 'submit' || el.type === 'image'))\n  );\n}","tryCatchPattern":"try { submit(target) } catch (e) { if (e instanceof Error && e.message.startsWith('Cannot submit element')) { submit(target.form) } else { throw e } }","preventionTips":["Always submit the <form> node (e.currentTarget.form) from control change handlers","Prefer data-based submit({ key: value }, { method: 'post' }) when you don't need DOM semantics","Type refs as HTMLFormElement when they will be submitted"],"tags":["forms","dom","submit","usesubmit","formdata"],"backgroundTag":"invalid-submit-target","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"}