aria-invalid
Indicates the entered value does not conform to the format expected by the application. Essential for form validation and communicating errors to screen reader users.
Overview
The aria-invalid attribute indicates that the value entered into an input field does not conform to the format expected by the application. This is essential for accessible form validation.
When aria-invalid="true" is set, screen readers announce that the field is invalid when the user focuses on it. This should be paired with aria-describedby or aria-errormessage to provide the actual error message.
Always Provide Error Details
aria-invalid only indicates that something is wrong—it doesn't explain what. Always use aria-describedby or aria-errormessage to link to an element containing the actual error message.
Live Demo: Form Validation
Input has aria-invalid="false"
Screen reader announcement: When focusing on an invalid field, screen readers announce: "Email Address, invalid entry, edit text". With aria-describedby linked, the error message is also read: "Please enter a valid email address".
Attribute Values
false(default)No error detected. The input value is valid or hasn't been validated yet. You can also simply omit the attribute for valid inputs.
trueThe value entered does not conform to the expected format. The input has failed validation. Should be paired with an error message.
grammarA grammatical error was detected in the text. Primarily used in text editors and word processors with grammar checking.
spellingA spelling error was detected in the text. Used in text editors with spell checking.
Code Examples
Basic Usage
<!-- Basic aria-invalid usage -->
<form>
<label for="email">Email address</label>
<input
type="email"
id="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<span id="email-error" role="alert">
Please enter a valid email address
</span>
</form>
<!-- aria-invalid values -->
<!-- "false" - No error (or attribute omitted) -->
<!-- "true" - Invalid input -->
<!-- "grammar" - Grammatical error detected -->
<!-- "spelling" - Spelling error detected -->All Value Types
<!-- aria-invalid value examples -->
<!-- No error state -->
<input type="email" aria-invalid="false" />
<!-- Or simply omit the attribute -->
<input type="email" />
<!-- Generic error -->
<input
type="text"
aria-invalid="true"
aria-describedby="error-msg"
/>
<span id="error-msg">This field is required</span>
<!-- Grammar error (text editors) -->
<textarea
aria-invalid="grammar"
aria-describedby="grammar-error"
>
Their going to the store
</textarea>
<span id="grammar-error">Suggestion: "They're" instead of "Their"</span>
<!-- Spelling error (text editors) -->
<input
type="text"
aria-invalid="spelling"
aria-describedby="spell-error"
value="Helllo world"
/>
<span id="spell-error">Misspelled: "Helllo" → "Hello"</span>Complete Form Pattern
<!-- Complete Form Validation Pattern -->
<form novalidate>
<!-- Required field -->
<div class="form-field">
<label for="name">
Full Name <span aria-hidden="true">*</span>
</label>
<input
type="text"
id="name"
required
aria-required="true"
aria-invalid="false"
aria-describedby="name-error"
/>
<span id="name-error" class="error" hidden>
Please enter your full name
</span>
</div>
<!-- Email with pattern validation -->
<div class="form-field">
<label for="email">
Email <span aria-hidden="true">*</span>
</label>
<input
type="email"
id="email"
required
aria-required="true"
aria-invalid="false"
aria-describedby="email-error"
/>
<span id="email-error" class="error" hidden>
Please enter a valid email address
</span>
</div>
<!-- Password with requirements -->
<div class="form-field">
<label for="password">Password</label>
<input
type="password"
id="password"
aria-invalid="false"
aria-describedby="password-hint password-error"
/>
<span id="password-hint" class="hint">
Must be at least 8 characters
</span>
<span id="password-error" class="error" hidden>
Password is too short
</span>
</div>
<button type="submit">Create Account</button>
</form>JavaScript Validation
// JavaScript Form Validation
const form = document.querySelector('form');
const emailInput = document.getElementById('email');
const emailError = document.getElementById('email-error');
// Validate on blur (when user leaves field)
emailInput.addEventListener('blur', function() {
validateEmail();
});
// Validate on input (real-time feedback)
emailInput.addEventListener('input', function() {
// Only validate if field was previously invalid
if (emailInput.getAttribute('aria-invalid') === 'true') {
validateEmail();
}
});
function validateEmail() {
const value = emailInput.value.trim();
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
if (!isValid && value.length > 0) {
emailInput.setAttribute('aria-invalid', 'true');
emailError.hidden = false;
emailError.setAttribute('role', 'alert'); // Announce error
} else {
emailInput.setAttribute('aria-invalid', 'false');
emailError.hidden = true;
emailError.removeAttribute('role');
}
}
// Validate entire form on submit
form.addEventListener('submit', function(e) {
let hasErrors = false;
// Check all required fields
form.querySelectorAll('[required]').forEach(input => {
if (!input.value.trim()) {
input.setAttribute('aria-invalid', 'true');
hasErrors = true;
}
});
if (hasErrors) {
e.preventDefault();
// Focus first invalid field
form.querySelector('[aria-invalid="true"]')?.focus();
}
});React Component
// React Form Validation Component
import { useState } from 'react';
function ValidatedInput({
label,
type = 'text',
validate,
errorMessage,
required,
...props
}) {
const [value, setValue] = useState('');
const [touched, setTouched] = useState(false);
const isInvalid = touched && !validate(value);
const inputId = props.id || props.name;
const errorId = `${inputId}-error`;
return (
<div className="form-field">
<label htmlFor={inputId}>
{label}
{required && <span aria-hidden="true"> *</span>}
</label>
<input
{...props}
type={type}
id={inputId}
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={() => setTouched(true)}
aria-invalid={isInvalid}
aria-describedby={isInvalid ? errorId : undefined}
aria-required={required}
className={isInvalid ? 'input-error' : ''}
/>
{isInvalid && (
<span
id={errorId}
role="alert"
className="error-message"
>
{errorMessage}
</span>
)}
</div>
);
}
// Usage
function SignupForm() {
return (
<form>
<ValidatedInput
name="email"
type="email"
label="Email Address"
required
validate={(v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)}
errorMessage="Please enter a valid email address"
/>
<ValidatedInput
name="password"
type="password"
label="Password"
required
validate={(v) => v.length >= 8}
errorMessage="Password must be at least 8 characters"
/>
<button type="submit">Sign Up</button>
</form>
);
}Best Practices
Always pair aria-invalid with aria-describedby or aria-errormessage to explain the error
Update aria-invalid dynamically as the user corrects their input
Use role="alert" on error messages for immediate screen reader announcement
Validate on blur (when user leaves field) rather than on every keystroke
Provide clear, specific error messages that explain how to fix the problem
Consider using aria-live="polite" for non-critical validation feedback
Don't set aria-invalid="true" without providing an explanation of the error
Don't validate too aggressively—let users finish typing before showing errors
Don't use color alone to indicate errors—always include text/icons
Don't forget to remove aria-invalid when the input becomes valid
Common Use Cases
Accessibility Notes
Screen Reader Announcements
When a field has aria-invalid="true", screen readers announce "invalid entry" or "invalid data" when the user focuses on it. This alerts users that attention is needed without interrupting their workflow.
Error Message Timing
For the best experience, show errors after the user finishes interacting with a field (on blur) rather than on every keystroke. Use role="alert" on error messages to announce them immediately, or aria-live="polite" for less urgent feedback.
Visual Design
Always provide multiple cues for invalid fields—don't rely solely on color. Use border changes, icons, and text messages together. Ensure error messages have sufficient color contrast (4.5:1 minimum for normal text).

