ErrorMessage
Validation text below a control. A sibling, not a Field part. id is required. There is no default role="alert" and no aria-live. invalid lives on the control or on Field.Root.
Mount the error only while it applies, and include its id in aria-describedby only then.
ErrorMessage owns its spacing. After a control, margin-block-start is field.label.gap (0.4rem). After Description, the sibling rule drops that to field.messages.gap (0.2rem). margin-block-end is 0.
const describedBy = invalid ? `${hintId} ${errorId}` : hintId
<Field.Root id={id} invalid={invalid} aria-describedby={describedBy}>
<Field.TextInput value={email} onChange={(event) => setEmail(event.target.value)} />
</Field.Root>
<Description id={hintId}>We only use this for billing receipts.</Description>
{invalid ? <ErrorMessage id={errorId}>Enter an address that contains an @.</ErrorMessage> : null}
To announce a submit-time error, pass role="alert" on this component. The package does not choose announcement policy.
When to use
When: Validation text below a control, after Description. Mount it and add its id to aria-describedby only while invalid; invalid lives on the control or Field.Root.
When not: Standing hints → Description. Showing while valid. Expecting auto-announce (no role="alert" by default; pass it for submit-time errors).
Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string | yes | — | Id the author lists in aria-describedby on the control or Field.Root. |
| children | React.ReactNode | no | — | Error text. |
| className | string | no | — | Optional class on the span. |