aria-describedby
Identifies the element (or elements) that describes the current element. Provides additional context or instructions that complement the accessible name.
Overview
The aria-describedby attribute identifies the element(s) that describe the current element. The description is announced by screen readers after the element's name and role, providing additional context.
Unlike aria-labelledby which provides the accessible name, aria-describedby provides supplementary information. It's commonly used for form field hints, error messages, and detailed descriptions.
Multiple IDs can be space-separated, and the descriptions are concatenated in the order they appear.
Live Demo: Form with Descriptions
aria-describedby connects form fields to their hints and error messages. Screen readers announce these descriptions after the field name, helping users understand what's expected.
Code Examples
Basic Usage
<!-- aria-describedby references elements that describe the current element -->
<!-- Form field with hint text -->
<label for="email">Email Address</label>
<input
type="email"
id="email"
aria-describedby="email-hint"
/>
<p id="email-hint">
We'll never share your email with anyone else.
</p>
<!-- Screen reader announces: "Email Address, edit text,
We'll never share your email with anyone else." -->Error Messages
<!-- Form validation with aria-describedby -->
<label for="password">Password</label>
<input
type="password"
id="password"
aria-describedby="password-requirements password-error"
aria-invalid="true"
/>
<!-- Requirements (always shown) -->
<p id="password-requirements">
Password must be at least 8 characters with one number.
</p>
<!-- Error message (shown when invalid) -->
<p id="password-error" class="error" role="alert">
Password is too short.
</p>
<!-- Multiple IDs can be space-separated -->
<!-- Screen reader announces both descriptions -->Multiple Descriptions
<!-- Combining multiple descriptions -->
<button
aria-describedby="btn-shortcut btn-tooltip"
>
Save Document
</button>
<span id="btn-shortcut" class="sr-only">
Keyboard shortcut: Ctrl+S
</span>
<span id="btn-tooltip" class="sr-only">
Saves the current document to the cloud
</span>
<!-- Both descriptions are announced together -->React Components
// React form with aria-describedby
import { useState, useId } from 'react';
interface FormFieldProps {
label: string;
type?: string;
hint?: string;
error?: string;
value: string;
onChange: (value: string) => void;
}
function FormField({
label,
type = 'text',
hint,
error,
value,
onChange
}: FormFieldProps) {
const id = useId();
const hintId = `${id}-hint`;
const errorId = `${id}-error`;
// Build aria-describedby from available descriptions
const describedBy = [
hint && hintId,
error && errorId,
].filter(Boolean).join(' ') || undefined;
return (
<div className="form-field">
<label htmlFor={id}>{label}</label>
<input
id={id}
type={type}
value={value}
onChange={(e) => onChange(e.target.value)}
aria-describedby={describedBy}
aria-invalid={!!error}
/>
{hint && (
<p id={hintId} className="hint">
{hint}
</p>
)}
{error && (
<p id={errorId} className="error" role="alert">
{error}
</p>
)}
</div>
);
}
// Password field with requirements
function PasswordField({ value, onChange, error }) {
const id = useId();
const requirementsId = `${id}-requirements`;
const errorId = `${id}-error`;
const describedBy = [
requirementsId,
error && errorId,
].filter(Boolean).join(' ');
return (
<div>
<label htmlFor={id}>Password</label>
<input
id={id}
type="password"
value={value}
onChange={(e) => onChange(e.target.value)}
aria-describedby={describedBy}
aria-invalid={!!error}
/>
<ul id={requirementsId} className="requirements">
<li>At least 8 characters</li>
<li>One uppercase letter</li>
<li>One number</li>
</ul>
{error && (
<p id={errorId} className="error" role="alert">
{error}
</p>
)}
</div>
);
}
// Dialog with description
function Dialog({ title, description, children, onClose }) {
const titleId = useId();
const descId = useId();
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby={titleId}
aria-describedby={descId}
>
<h2 id={titleId}>{title}</h2>
<p id={descId}>{description}</p>
{children}
<button onClick={onClose}>Close</button>
</div>
);
}
