required
Prevents form submission when a field is empty. Integrates with constraint validation, CSS pseudo-classes, and screen reader announcements.
Overview
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>
Values
| 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>
Select Elements
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>
Radio Groups
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>
Checkboxes
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>
CSS Pseudo-Classes
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.
Custom Validation Messages
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.
Bypassing Validation
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.
Accessibility
- Screen readers announce required fields as "required". You do not need
aria-required="true"when the nativerequiredattribute is present — it is redundant. - Always pair the
requiredattribute with a visible indicator (like an asterisk*) so sighted users can identify required fields without submitting first. - Use
aria-hidden="true"on decorative asterisks so screen readers do not announce "star" alongside "required". - Native validation messages are automatically announced by screen readers when the form is submitted with invalid fields.
Limitations
requiredis a boolean attribute.required="false"still makes the field required. Remove the attribute entirely to make it optional.- Constraint validation does not run on
disabledorreadonlyfields, even if they have therequiredattribute. - Validation only runs on form submit (or when you call
checkValidity()/reportValidity()in JavaScript). Typing in a field does not trigger it. - The
novalidateattribute on the form orformnovalidateon a button bypasses all constraint validation, includingrequired. - Native validation tooltips cannot be styled with CSS. If you need custom styling, use
novalidateon the form and build your own validation UI.
See Also
pattern— regex-based validationnovalidate— bypass validation per form or per buttondisabled— disabling skips validation entirely<form>element reference<input>element reference