Accessible form descriptions
A form control needs to reference helper or validation text through aria-describedby.
React 19+ · Hook
useId generates unique strings for connecting elements through accessibility attributes. Call it at the top level of a component or custom Hook, with no arguments.
Call useId at the top level of a component or custom Hook to obtain a stable unique string for the mounted component. Pass that string to related accessibility attributes, such as aria-describedby, so elements can reference one another without manually coordinating IDs.
const id = useId();A unique ID string associated with the particular useId call in the component.
import { useId } from 'react';
export default function PasswordField() {
const passwordHintId = useId();
return (
<>
<label htmlFor="password">Password</label>
<input id="password" type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>Use at least 12 characters.</p>
</>
);
}Generates a component-specific ID and connects the input to its accessibility hint.
A form control needs to reference helper or validation text through aria-describedby.
Several component instances need internally coordinated IDs without manually passing them from a parent.
Server-rendered markup must preserve matching control-to-description references across hydration.
useId generates a unique ID string associated with a particular useId call in a component. The intended use is connecting elements through accessibility attributes.
Follow-up: Why not use it for list keys? Keys should come from the underlying data, while useId is intended for IDs in the rendered accessibility relationship.
No parameters are required. Call it at the component or custom Hook's top level, not inside a loop or condition.
Follow-up: How would you handle a variable number of repeated fields? Extract each repeated field into a component so each instance can call the Hook at its own top level.
With server rendering, the server and client must render an identical component tree. If their trees differ, the generated IDs will not match.
Follow-up: What is the practical implication for hydration? Keep conditional rendering and component structure consistent between server and client, especially around components using useId.
useuseStateuseRef