Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Prevents form submission when a field is empty. Integrates with constraint validation, CSS pseudo-classes, and screen reader announcements.
The required attribute prevents form submission when a field is empty. The browser checks the constraint on submit and shows a native error message if the field has no value.
This is the foundation of client-side form validation. It works without JavaScript, integrates with CSS pseudo-classes for styling, and is announced by screen readers.
Applies to: <input>, <select>, <textarea>
| Element | What "empty" means |
|---|---|
| Text input | Value is an empty string |
<textarea> | Value is an empty string |
<select> | Selected option has value="" |
| Checkbox | Not checked |
| Radio group | No radio in the group is selected |
| File input | No file selected |
<form class="stacked"> <label for="name">Full Name <span aria-hidden="true">*</span></label> <input type="text" id="name" name="name" required /> <label for="email">Email <span aria-hidden="true">*</span></label> <input type="email" id="email" name="email" required /> <label for="bio">Bio (optional)</label> <textarea id="bio" name="bio" rows="3"></textarea> <button type="submit">Submit</button></form>
For <select required>, the first option must have an empty value attribute. This serves as the placeholder. The browser considers the field invalid as long as the empty-value option is selected.
<label for="country">Country <span aria-hidden="true">*</span></label><select id="country" name="country" required> <option value="">-- Select a country --</option> <option value="US">United States</option> <option value="CA">Canada</option> <option value="GB">United Kingdom</option></select>
Adding required to any radio button in a group makes the entire group required — at least one radio must be selected. You only need required on one radio, but adding it to the first one is a common convention for readability.
<fieldset> <legend>Subscription Plan <span aria-hidden="true">*</span></legend> <label> <input type="radio" name="plan" value="free" required /> Free </label> <label> <input type="radio" name="plan" value="pro" /> Pro </label> <label> <input type="radio" name="plan" value="enterprise" /> Enterprise </label></fieldset>
A required checkbox must be checked for the form to submit. This is the standard pattern for "I agree to the terms" checkboxes.
<form class="stacked"> <label> <input type="checkbox" name="terms" required /> I agree to the Terms of Service <span aria-hidden="true">*</span> </label> <button type="submit">Create Account</button></form>
The required attribute activates several CSS pseudo-classes for validation styling.
| Pseudo-class | Matches when |
|---|---|
:required | Field has the required attribute |
:optional | Field does not have required |
:valid | Field satisfies all constraints |
:invalid | Field violates a constraint (including required) |
:user-invalid | Invalid after user has interacted with the field |
/* Visual indicator for required fields */input:required,select:required,textarea:required { border-left: 3px solid var(--color-primary);} /* Valid required field */input:required:valid { border-left-color: var(--color-success);} /* Invalid after user interaction */input:required:user-invalid { border-left-color: var(--color-error);} /* Optional fields get no special treatment */input:optional { border-left: 3px solid transparent;}
Prefer :user-invalid over :invalid. The :invalid pseudo-class applies immediately on page load, which means empty required fields show error styles before the user has done anything. The :user-invalid pseudo-class waits until the user has interacted with the field.
Native validation messages are functional but generic. Use setCustomValidity() to provide context-specific messages.
const email = document.querySelector('#email'); email.addEventListener('input', () => { if (email.validity.valueMissing) { email.setCustomValidity('We need your email to send the confirmation.'); } else if (email.validity.typeMismatch) { email.setCustomValidity('This does not look like an email address.'); } else { email.setCustomValidity(''); // Clear — field is valid }});
Call setCustomValidity('') (empty string) to mark the field as valid. Forgetting this step is a common bug — the field stays invalid even after the user corrects the value.
Use formnovalidate on a submit button to bypass all constraint validation for that specific submission. This is ideal for "Save Draft" buttons where incomplete data is acceptable.
<form class="stacked"> <label for="draft-title">Title</label> <input type="text" id="draft-title" name="title" required /> <label for="draft-body">Body</label> <textarea id="draft-body" name="body" rows="4" required></textarea> <footer class="actions end"> <!-- This button validates --> <button type="submit">Publish</button> <!-- This button skips validation --> <button type="submit" formnovalidate>Save Draft</button> </footer></form>
See novalidate for more on validation bypass patterns.
aria-required="true" when the native required attribute is present — it is redundant.required attribute with a visible indicator (like an asterisk *) so sighted users can identify required fields without submitting first.aria-hidden="true" on decorative asterisks so screen readers do not announce "star" alongside "required".required is a boolean attribute. required="false" still makes the field required. Remove the attribute entirely to make it optional.disabled or readonly fields, even if they have the required attribute.checkValidity() / reportValidity() in JavaScript). Typing in a field does not trigger it.novalidate attribute on the form or formnovalidate on a button bypasses all constraint validation, including required.novalidate on the form and build your own validation UI.pattern — regex-based validationnovalidate — bypass validation per form or per buttondisabled — disabling skips validation entirely<form> element reference<input> element reference