novalidate

Bypasses native constraint validation on form submission. Use on the form element or per-button with formnovalidate.

Overview

The novalidate attribute on a <form> element bypasses all native constraint validation when the form is submitted. Fields with required, pattern, min/max, type="email", and other constraints will not be checked. No native error tooltips will appear.

The formnovalidate attribute does the same thing but on a per-button basis — only submissions triggered by that specific button skip validation.

Applies to: novalidate on <form>; formnovalidate on <button type="submit"> and <input type="submit">

Values

AttributeOn ElementEffect
novalidate<form>Skips all validation for every submission
formnovalidateSubmit buttonSkips validation for this button only

Form-Level: novalidate

Adding novalidate to a form disables constraint validation entirely. The form will submit regardless of whether fields are empty, malformed, or out of range.

<!-- All constraint validation is skipped on submit --> <form novalidate class="stacked"> <label for="email">Email</label> <input type="email" id="email" name="email" required /> <label for="age">Age</label> <input type="number" id="age" name="age" min="18" max="120" required /> <button type="submit">Submit</button> </form>

The validation attributes (required, pattern, etc.) remain in the HTML. They still provide meaning and can still be checked programmatically via JavaScript — they just do not block submission.

Button-Level: formnovalidate

The formnovalidate attribute on a submit button skips validation only for that button. Other submit buttons on the same form still validate normally. This is the standard pattern for "Save Draft" alongside "Publish".

<form action="/articles" method="post" class="stacked"> <label for="title">Title</label> <input type="text" id="title" name="title" required /> <label for="body">Body</label> <textarea id="body" name="body" rows="6" required></textarea> <footer class="actions end"> <!-- Validates: title and body must be filled --> <button type="submit">Publish</button> <!-- Skips validation: saves incomplete drafts --> <button type="submit" formnovalidate formaction="/articles/draft"> Save Draft </button> </footer> </form>

This is more useful than form-level novalidate in most cases. You get full validation for the primary action and a bypass for secondary actions.

Custom Validation UI

The most common reason to use novalidate is to replace native validation tooltips with a custom error display. Native tooltips cannot be styled and vary across browsers. With novalidate, you suppress them and build your own.

<form novalidate class="stacked" id="signup-form"> <form-field> <label for="signup-email">Email</label> <input type="email" id="signup-email" name="email" required /> <small class="error" hidden></small> </form-field> <form-field> <label for="signup-pw">Password</label> <input type="password" id="signup-pw" name="password" minlength="8" required /> <small class="error" hidden></small> </form-field> <button type="submit">Create Account</button> </form> const form = document.querySelector('#signup-form'); form.addEventListener('submit', (e) => { e.preventDefault(); // Clear previous errors form.querySelectorAll('.error').forEach(el => { el.hidden = true; el.textContent = ''; }); let isValid = true; // Check each field using the native validity API for (const field of form.elements) { if (field.validity && !field.validity.valid) { isValid = false; const error = field.closest('form-field')?.querySelector('.error'); if (error) { error.textContent = getCustomMessage(field); error.hidden = false; } } } if (isValid) { form.submit(); } }); function getCustomMessage(field) { if (field.validity.valueMissing) return 'This field is required.'; if (field.validity.typeMismatch) return 'Please enter a valid value.'; if (field.validity.tooShort) return `Must be at least ${field.minLength} characters.`; return field.validationMessage; // Fallback to browser default }

The native validity API (field.validity, checkValidity(), reportValidity()) still works even with novalidate. You are suppressing the browser's automatic checking on submit, not disabling the validation engine itself.

Progressive Enhancement

The best pattern: write the form with native validation attributes, let it work without JavaScript, then upgrade it with custom validation UI when JavaScript is available.

<!-- Server validates everything. Client validation is a bonus. --> <form action="/register" method="post" class="stacked"> <label for="pe-email">Email</label> <input type="email" id="pe-email" name="email" required /> <label for="pe-pw">Password</label> <input type="password" id="pe-pw" name="password" minlength="8" required /> <button type="submit">Register</button> </form> // Upgrade the form to use custom validation UI const form = document.querySelector('form'); // Only disable native validation if JS is running form.noValidate = true; form.addEventListener('submit', (e) => { if (!form.checkValidity()) { e.preventDefault(); // Show custom error UI... } });

Set noValidate via JavaScript (not in the HTML) so that the form still validates natively when JavaScript fails to load. The server handles validation either way.

Accessibility

  • Native validation messages are automatically announced by screen readers. If you suppress them with novalidate, your custom error UI must be equally accessible — use aria-describedby, aria-invalid="true", and role="alert" or live regions.
  • Custom validation gives you more control over the timing and wording of error messages, which can be an accessibility improvement over the terse native messages.
  • Ensure custom error messages are visible and adjacent to the field they describe. Do not only rely on color to indicate errors.

Limitations

  • novalidate is a boolean attribute. Writing novalidate="false" still disables validation. Remove the attribute entirely to re-enable native validation.
  • novalidate only affects constraint validation on submit. It does not prevent <input type="number"> from rejecting non-numeric keystrokes or maxlength from limiting input length.
  • novalidate does not affect server-side validation. Always validate on the server regardless of client-side settings.
  • The JavaScript property is form.noValidate (camelCase with capital V), not form.novalidate.

See Also