Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTEWidget Attributes

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.

Value Type
ID Reference
Requires
aria-invalid="true"
Alternative
aria-describedby

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 typing

React 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

Form field validation errors
Required field notifications
Format validation (email, phone)
Password requirements feedback
Character/word count limits
Date range validation
Matching field validation
Server-side validation feedback

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.

Related Attributes

Specifications & Resources