aria-errormessage
Identifies the element that provides an error message for the object. Only announced when aria-invalid is true, making it perfect for form validation.
Overview
The aria-errormessage attribute identifies the element that provides an error message for the object. The key difference from aria-describedby is that the error message is only exposed when aria-invalid="true".
This is especially useful for form validation because you can have both a hint (using aria-describedby) that's always announced, and an error message (using aria-errormessage) that only gets announced when the field is invalid.
Browser Support Note
aria-errormessage is relatively new and may not be fully supported in all screen reader/browser combinations. Consider using aria-describedby as a fallback or in addition to aria-errormessage.
Live Demo: Error Message Association
Must be at least 8 characters
Input has aria-invalid="false", aria-describedby="demo-password-hint", aria-errormessage="demo-password-error"
Screen reader behavior: The hint ("Must be at least 8 characters") is announced every time the field is focused. The error message ("Password is too short") is only announced when aria-invalid="true".
aria-errormessage vs aria-describedby
aria-errormessage
- • Only announced when aria-invalid="true"
- • Specifically designed for error messages
- • Error element can stay in DOM (hidden)
- • References single error element ID
- • Newer attribute, limited support
Use when: You want the error message to only be announced when the field is invalid.
aria-describedby
- • Always announced when element focused
- • General-purpose description attribute
- • Can reference multiple element IDs
- • Well-supported across all platforms
- • Reads all referenced text every time
Use when: You need a fallback or want hints that are always read.
Recommended Pattern: Use Both
For the best experience, use aria-describedby for hints that should always be read, and aria-errormessage for errors that only appear when invalid. This provides clear separation of concerns and optimal user experience.
Code Examples
Basic Usage
<!-- Basic aria-errormessage usage -->
<label for="password">Password</label>
<input
type="password"
id="password"
aria-invalid="true"
aria-errormessage="password-error"
/>
<span id="password-error">
Password must be at least 8 characters long
</span>
<!-- Key points:
- aria-errormessage references the ID of the error element
- Only announces when aria-invalid="true"
- The error element can be hidden until needed
-->Comparison with aria-describedby
<!-- aria-errormessage vs aria-describedby -->
<!-- aria-describedby: ALWAYS read when focusing -->
<label for="email1">Email</label>
<input
type="email"
id="email1"
aria-describedby="email1-hint email1-error"
/>
<span id="email1-hint">We'll never share your email</span>
<span id="email1-error" hidden>Invalid email format</span>
<!-- Screen reader reads BOTH hint and error every time -->
<!-- aria-errormessage: Only read when aria-invalid="true" -->
<label for="email2">Email</label>
<input
type="email"
id="email2"
aria-invalid="false"
aria-describedby="email2-hint"
aria-errormessage="email2-error"
/>
<span id="email2-hint">We'll never share your email</span>
<span id="email2-error" hidden>Invalid email format</span>
<!-- Screen reader only reads hint; error read ONLY when invalid -->
<!-- BEST: Use both together -->
<input
type="email"
id="email"
aria-invalid="false"
aria-describedby="email-hint"
aria-errormessage="email-error"
/>
<!-- Hint always read, error only when invalid -->Complete Form Pattern
<!-- Complete Form with Error Messages -->
<form novalidate>
<!-- Username field -->
<div class="form-field">
<label for="username">
Username <span aria-hidden="true">*</span>
</label>
<input
type="text"
id="username"
required
aria-required="true"
aria-invalid="false"
aria-describedby="username-hint"
aria-errormessage="username-error"
/>
<span id="username-hint" class="hint">
3-20 characters, letters and numbers only
</span>
<span id="username-error" class="error" hidden>
Username must be 3-20 alphanumeric characters
</span>
</div>
<!-- Password field -->
<div class="form-field">
<label for="password">
Password <span aria-hidden="true">*</span>
</label>
<input
type="password"
id="password"
required
aria-required="true"
aria-invalid="false"
aria-describedby="password-hint"
aria-errormessage="password-error"
/>
<span id="password-hint" class="hint">
Minimum 8 characters with at least one number
</span>
<span id="password-error" class="error" hidden>
Password doesn't meet requirements
</span>
</div>
<button type="submit">Create Account</button>
</form>
<style>
.error {
color: #dc2626;
display: flex;
align-items: center;
gap: 0.5rem;
}
.error[hidden] {
display: none;
}
input[aria-invalid="true"] {
border-color: #dc2626;
}
</style>JavaScript Validation
// JavaScript: Showing/Hiding Error Messages
const usernameInput = document.getElementById('username');
const usernameError = document.getElementById('username-error');
function validateUsername() {
const value = usernameInput.value;
const isValid = /^[a-zA-Z0-9]{3,20}$/.test(value);
if (!isValid && value.length > 0) {
// Show error
usernameInput.setAttribute('aria-invalid', 'true');
usernameError.hidden = false;
} else {
// Hide error
usernameInput.setAttribute('aria-invalid', 'false');
usernameError.hidden = true;
}
}
// Validate on blur
usernameInput.addEventListener('blur', validateUsername);
// Re-validate as user types (only if already invalid)
usernameInput.addEventListener('input', () => {
if (usernameInput.getAttribute('aria-invalid') === 'true') {
validateUsername();
}
});
// Important: Error is NOT announced until aria-invalid is true
// This prevents premature error announcements while typingReact Component
// React Component with aria-errormessage
import { useState } from 'react';
function FormField({
label,
type = 'text',
hint,
validate,
errorMessage,
required,
...props
}) {
const [value, setValue] = useState('');
const [touched, setTouched] = useState(false);
const isInvalid = touched && value.length > 0 && !validate(value);
const fieldId = props.id || props.name;
const hintId = `${fieldId}-hint`;
const errorId = `${fieldId}-error`;
return (
<div className="form-field">
<label htmlFor={fieldId}>
{label}
{required && <span aria-hidden="true"> *</span>}
</label>
<input
{...props}
type={type}
id={fieldId}
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={() => setTouched(true)}
aria-required={required}
aria-invalid={isInvalid}
aria-describedby={hint ? hintId : undefined}
aria-errormessage={errorId}
className={isInvalid ? 'input-error' : ''}
/>
{hint && (
<span id={hintId} className="hint">
{hint}
</span>
)}
<span
id={errorId}
className="error"
hidden={!isInvalid}
role={isInvalid ? 'alert' : undefined}
>
{errorMessage}
</span>
</div>
);
}
// Usage
function SignupForm() {
return (
<form>
<FormField
name="username"
label="Username"
required
hint="3-20 characters, letters and numbers only"
validate={(v) => /^[a-zA-Z0-9]{3,20}$/.test(v)}
errorMessage="Username must be 3-20 alphanumeric characters"
/>
<FormField
name="password"
type="password"
label="Password"
required
hint="At least 8 characters with a number"
validate={(v) => v.length >= 8 && /\d/.test(v)}
errorMessage="Password must be at least 8 characters with a number"
/>
<button type="submit">Sign Up</button>
</form>
);
}Best Practices
Always pair aria-errormessage with aria-invalid for proper announcement timing
Use aria-describedby for hints and aria-errormessage for errors
Keep error messages visible in the DOM (use hidden attribute rather than removing)
Write clear, specific error messages that tell users how to fix the problem
Consider adding role="alert" to error messages for immediate announcement
Test with multiple screen readers due to varying support levels
Don't forget to set aria-invalid="true" when showing the error
Don't use aria-errormessage without an error element in the DOM
Don't rely solely on aria-errormessage—use aria-describedby as fallback
Don't use vague error messages like "Invalid input"
Common Use Cases
Accessibility Notes
Conditional Announcement
The error message referenced by aria-errormessage is only announced when aria-invalid="true". This prevents screen readers from reading error messages before the user has had a chance to interact with the field.
Browser Support
aria-errormessage is defined in ARIA 1.1+ but support varies. NVDA and JAWS have good support with recent versions. VoiceOver support is improving. For maximum compatibility, also include aria-describedby pointing to the same error element when it's visible.
Live Regions for Immediate Feedback
For errors that should be announced immediately (not just when the field is focused), add role="alert" to the error element. This creates a live region that announces the error as soon as it appears in the DOM.

