The HTML novalidate attribute
Adding novalidate to a <form> lets it submit with invalid fields and no error bubbles. The fields still know they're invalid, so checkValidity() and :invalid keep working. Your script decides what to show and whether to send.
Overview
The HTML novalidate attribute stops a form from validating its fields when it is submitted. It is a boolean attribute of the <form> element, and while it is present an invalid form submits anyway and the browser shows no error bubbles.
Each field still tracks its own validity. The attribute disables neither the constraint validation API nor pseudo-classes such as :valid, so an empty required field still matches :invalid and makes checkValidity() return false.
No attribute switches validation back on for a single button. The check is skipped when the form has novalidate or the pressed button has formnovalidate, so every submit button and the Enter key send a novalidate form unchecked.
Syntax
<form action="/subscribe" method="post" novalidate>
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
<button>Subscribe</button>
</form>
Values
| Value |
|---|
| Its presence turns off validation for every submission of the form. |
Example
<form id="join" novalidate>
<label for="email">Email</label>
<input type="email" id="email" name="email" required><br>
<label for="age">Age</label>
<input type="number" id="age" name="age" min="18" value="16" style="width:4em">
<button>Join</button>
</form>
<p id="msg" role="status" style="overflow-wrap:anywhere"></p>
<p>Press Join as it is, then enter an email address and an age of 18 or more.</p>
<script>
const form = document.getElementById('join');
form.addEventListener('submit', function (e) {
e.preventDefault();
const email = form.elements.email;
const age = form.elements.age;
const problems = [];
if (email.validity.valueMissing) problems.push('Enter an email address.');
else if (!email.validity.valid) problems.push('Check the email address.');
if (age.validity.rangeUnderflow) problems.push('You must be 18 or older.');
else if (!age.validity.valid) problems.push('Enter a whole number for the age.');
document.getElementById('msg').textContent = problems.length
? 'The form submitted anyway. ' + problems.join(' ')
: 'All fields are valid. It would send ' + new URLSearchParams(new FormData(form));
});
</script>
Best practices
- Add novalidate only with a submit handler that checks the fields and calls
preventDefault()while one is invalid. Without that handler, the form sends the fields unchecked. - Build each message from the field's
validityflags, such asvalueMissingortypeMismatch, so your messages follow the same rules the browser applies. - Rely on
:user-invalidto flag the fields that failed. A submit attempt marks every field as interacted with before novalidate is checked, so the selector matches even with validation off. - Call
form.reportValidity()to fall back on the browser's own messages. It still shows them under novalidate and returns false while any field is invalid. - Test your server's checks by submitting a novalidate form with bad data. Servers should not rely on client-side validation, since it can be bypassed.
Accessibility
Under novalidate, the browser no longer reports errors itself, so your page has to. Put each message in an element that already has aria-live="polite" or role="status" when the page loads, so that it is presented to everyone, including screen reader users.























