"My form doesn't submit."
Three causes, three fixes.
You clicked submit. Nothing happened. Or the page reloaded with an empty URL. Or the data never reached the server. The form element has been stable for 25 years — the bug is almost always one of three things: the button isn't a submit button, JavaScript is calling preventDefault, or the inputs have no name attribute. This page covers each, with the exact fix.
Short answer: Check three things. (1) The submit button must be <button type="submit"> or <input type="submit"> — not type="button". (2) If JavaScript calls event.preventDefault(), the form never navigates — either remove it, or send the data with fetch. (3) Every input needs a name attribute — otherwise its value is not included in the submission. The form element itself is almost never the problem.
✅ Cause 1 — Button isn't type="submit"
The most common cause: the button has the wrong type.
<form action="/submit" method="POST"> <input name="email" type="email"> <button type="button">Submit</button> </form>
❌ type="button" does not submit the form. ❌ Clicking "Submit" does nothing. ❌ No network request, no navigation.
<form action="/submit" method="POST"> <input name="email" type="email"> <button type="submit">Submit</button> </form>
✅ Clicking "Submit" fires the submit event. ✅ Browser sends the form to /submit via POST. ✅ Page navigates to the response.
✅ Cause 2 — JavaScript calls preventDefault
The handler blocks the submission.
<form id="signup" action="/submit" method="POST"> <input name="email" type="email"> <button type="submit">Submit</button> </form>
// BAD: unconditional preventDefault — form never submits
document.getElementById('signup').addEventListener('submit', (e) => {
e.preventDefault();
// ...but nothing sends the data
});
// GOOD: only preventDefault when you handle submission yourself
document.getElementById('signup').addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(e.target);
await fetch('/api/signup', { method: 'POST', body: data });
// handle success/error
});
Don't call preventDefault unless you send the data yourself. The most common bug in React forms and vanilla JS forms is an unconditional e.preventDefault() in the submit handler, with no fetch or XMLHttpRequest after it. The form fires the submit event, your handler runs, and then nothing happens — no navigation, no network request. Either remove the preventDefault (let the form submit normally) or add the fetch call.
✅ Cause 3 — Input has no name attribute
The value is not sent to the server.
<form action="/submit" method="POST"> <!-- ❌ no name — value not sent --> <input id="email" type="email"> <!-- ✅ name present — value is sent --> <input name="email" id="email" type="email"> <button type="submit">Submit</button> </form>
❌ Without name: input is invisible to the server. ✅ With name: value appears in the POST body. ✅ id is for labels and CSS — name is for the data.
❓ Frequently asked
Why is my form not submitting?
Three most common causes. (1) The submit button is type="button", not type="submit" — clicking it does nothing. Fix: use type="submit", or omit type (submit is the default for button inside a form). (2) JavaScript calls event.preventDefault() unconditionally — the form submits, but the page never navigates. Fix: only preventDefault when validation fails, or remove it. (3) The input has no name attribute — the value isn't sent to the server. Fix: add name to every input you want submitted.
What is the default type of a button inside a form?
submit. If you write button Click inside a form element and omit the type attribute, clicking it submits the form. This is a common source of surprise: a button that looks like a secondary action submits the form because type defaults to submit. If you want a non-submitting button, write type="button" explicitly. Omitting type is not the same as writing type="button".
Why is my input value not sent to the server?
The input has no name attribute. Only inputs with a name are included in the form submission. An input with id but no name is invisible to the server. Add name to every input, select, and textarea you want submitted. The name becomes the key in the query string or POST body. The id is for labels and CSS — name is for the data.
Does preventDefault stop the form from submitting?
Yes. If you call event.preventDefault() in the submit handler, the browser does not navigate and does not send the form. The submit event still fires — your JavaScript runs — but the default action is blocked. If your handler always calls preventDefault and never sends the data (via fetch or XMLHttpRequest), the form appears to do nothing. Only call preventDefault when you intend to handle submission yourself.
Why does clicking submit reload the page instead of submitting?
The form submits normally and the browser navigates to the action URL. If you did not expect navigation, either the action is empty (which reloads the current page) or your JavaScript did not call preventDefault. This is default HTML behavior — not a bug. If you want to submit via JavaScript, call preventDefault and send the data with fetch. Otherwise, set a proper action URL so navigation goes to the right place.
What are the required attributes for a form to submit?
A form needs an action (where to submit) and a method (GET or POST). The action defaults to the current URL, and method defaults to GET. A submit button is required for user-triggered submission — either button type="submit" or input type="submit". Every input you want included needs a name. The enctype is needed only for file uploads (multipart/form-data). Without a name on an input, its value is not sent.