<form> that is inside the document. The warning appears when the form was never appended (built with document.createElement) or was removed before submission (a React, Vue or Svelte re-render, a closing modal, a wizard step). Fix it by appending the form first, or by calling e.preventDefault() and sending FormData with fetch before you change the UI.Point any HTML form at one endpoint. 500 free submissions — no card, no time limit — and unlimited forms.
Create free accountWhat the warning means
Open the console and you see: Form submission canceled because the form is not connected. Under the HTML spec's form submission algorithm, the browser first checks that the <form> element is connected, meaning it sits inside a document. If it is not, the submission stops right there: no request, no navigation, no error thrown to your code, just this console message.
So the question is always the same: at the moment the browser tried to submit, where was the form? Either it was never inserted, or something removed it. You can check with form.isConnected. The four causes below cover almost every case.
Cause 1: a form built with document.createElement and never appended
A common trick for a POST download or a redirect to a payment page is to build a form in JavaScript and call submit(). If you skip the append, the form lives only in memory:
// Broken: the form is not in the document
const form = document.createElement("form");
form.method = "POST";
form.action = "/export";
const input = document.createElement("input");
input.name = "id";
input.value = "42";
form.appendChild(input);
form.submit(); // Form submission canceled because the form is not connectedFix: append it before submitting, then remove it if you want to keep the DOM clean.
document.body.appendChild(form);
form.submit();
form.remove();If you do not actually need the browser to navigate, drop the form entirely and send the data with fetch. A FormData object works without any element in the page:
const data = new FormData();
data.append("id", "42");
await fetch("/export", { method: "POST", body: data });The difference between submit() and requestSubmit() (events and validation) is covered in form.submit() vs requestSubmit(). Both need a connected form.
Cause 2: React, Vue or Svelte removes the form before the browser submits
This is the most common source of the warning. The click on a submit button runs your handler first, and only afterwards does the browser start the native submission. If the handler changes state so the component re-renders without the form (for example swapping in a "thank you" view), the form is gone by the time the browser gets to it.
// Broken (React): the form unmounts before the native submit
function Contact() {
const [sent, setSent] = useState(false);
if (sent) return <p>Thanks!</p>;
return (
<form action="/contact" method="POST">
<input name="email" type="email" required />
<button type="submit" onClick={() => setSent(true)}>Send</button>
</form>
);
}Fix: handle the submit event, call e.preventDefault(), send the data yourself, and change state only after the request finishes.
function Contact() {
const [sent, setSent] = useState(false);
async function onSubmit(e) {
e.preventDefault(); // stop the native submit
const form = e.currentTarget;
const res = await fetch(form.action, {
method: "POST",
body: new FormData(form),
});
if (res.ok) setSent(true); // now it is safe to unmount
}
if (sent) return <p>Thanks!</p>;
return (
<form action="/contact" method="POST" onSubmit={onSubmit}>
<input name="email" type="email" required />
<button type="submit">Send</button>
</form>
);
}The same rule applies in Vue (@submit.prevent, then flip a v-if after fetch) and Svelte (call preventDefault() in the submit handler, then change the {#if} state). While the request is in flight, disable the button so a second click does not send twice; see preventing duplicate form submissions.
Cause 3: a submit button inside a modal or step that unmounts on click
Multi-step forms and modal forms often render each step as its own component. If the "Next" or "Finish" button is a submit button and its click handler moves to the next step or closes the modal, the step's <form> unmounts in the same tick and the browser cancels the submission.
- Give buttons that only navigate between steps
type="button", so they never start a submission. - Keep one
<form>around all steps and show or hide the steps inside it, instead of a separate form per step. - On the final step, submit with
preventDefault()plusfetch, then close the modal once the response comes back.
Cause 4: type=submit on a button that also closes a dialog
A <button> inside a form defaults to type="submit". Put a "Cancel" or "Close" button in a form inside a dialog, wire its click to close and remove the dialog, and the browser tries to submit a form that has just been removed. Two clean fixes:
<!-- Buttons that only close: not a submit button -->
<button type="button" onclick="dialog.close()">Cancel</button>
<!-- Or let the dialog close itself, without removing the form -->
<dialog id="dialog">
<form method="dialog">
<button value="cancel">Cancel</button>
</form>
</dialog>method="dialog" closes the dialog and keeps the form in the document, so nothing is canceled. If your framework removes the dialog from the DOM when it closes, do that after the dialog's close event, not inside the button's click handler.
Quick debugging checklist
- Right before the submit, log
form.isConnected.falseconfirms this bug. - Search for
document.createElement("form")and check thatappendChildruns beforesubmit(). - Look at what the submit button's
onClickdoes. Any state change that hides the form belongs after the request, not before it. - Check every
<button>inside the form has an explicittype. Only the real send button should betype="submit". - In React DevTools or Vue DevTools, watch whether the form component unmounts on click.
- Still not sending, but no warning? Work through why your HTML form is not submitting (missing
nameattributes, buttons outside the form, wrongmethod).
Or skip the native submit and POST to a form endpoint
Most of these bugs disappear when the browser never runs a native submission at all. Cancel it, send FormData with fetch, and update the UI when the response arrives. Here is the pattern pointed at the splitforms endpoint:
<form id="contact" action="https://splitforms.com/api/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
<p id="status" role="status"></p>
</form>
<script>
const form = document.getElementById("contact");
const status = document.getElementById("status");
form.addEventListener("submit", async (e) => {
e.preventDefault(); // no native submit, nothing to cancel
status.textContent = "Sending...";
const res = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
const result = await res.json();
if (res.ok && result.success === true) {
form.reset(); // safe to hide or remove the form now
status.textContent = "Thanks! Your message was sent.";
} else {
status.textContent = result.message || "Could not send. Please try again.";
}
});
</script>Status codes and error bodies are listed in the response guide. Get a free access key.
FAQ
What does 'Form submission canceled because the form is not connected' mean?
The browser was asked to submit a form that is no longer (or never was) part of the document. The HTML spec says a form that is not connected to a document cannot be submitted, so Chrome cancels the navigation and logs this warning instead. Nothing is sent to the server.
Why do I see this warning in React?
Usually a click or submit handler changes state (setSubmitted(true), closing a modal, moving to the next step) and React removes the <form> before the browser runs its native submission. Call e.preventDefault() in onSubmit, send the data with fetch and FormData, and only change state after the request finishes.
How do I submit a form created with document.createElement?
Append it to the page first: document.body.appendChild(form), then form.submit(), then optionally form.remove(). A form that is only held in a JavaScript variable is not connected, so submit() is canceled. If you do not need a page navigation, skip the form and POST a FormData object with fetch.
How can I check whether a form is connected?
Read form.isConnected in the console or in your handler. It is true when the element is inside a document and false when it was never inserted or has been removed. Logging it right before the submit call shows whether your code removes the form too early.
References: HTML spec: form submission algorithm and MDN: Node.isConnected.