Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTEWidget Attributes

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.

Value Type
true | false | grammar | spelling
Common Use
Form Validation
Pair With
aria-errormessage

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.

true

The value entered does not conform to the expected format. The input has failed validation. Should be paired with an error message.

grammar

A grammatical error was detected in the text. Primarily used in text editors and word processors with grammar checking.

Example: "Their going to the store" → suggests "They're"
spelling

A spelling error was detected in the text. Used in text editors with spell checking.

Example: "Helllo world" → suggests "Hello"

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

Required field validation
Email format validation
Password strength requirements
Phone number format checking
Credit card number validation
Date/time format validation
Username availability checking
Spell checking in text editors

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).

Related Attributes

Specifications & Resources