Label, description, and error for any form control, wrapping the Base UI Field. Drop a Bones control inside and the accessibility wiring and validation state happen automatically.

Playground

Flip Invalid to watch the error replace the description and the input turn red at the same time; click the label to prove it focuses the input.

We only use this for receipts.

States

Invalid and disabled are the ones you set; touched, dirty, and filled arrive on their own as the user works, and focus rings the control inside on every focus.

We only use this for receipts.

That doesn't look like an email address.

Managed by your workspace admin.

Beyond the manual invalid prop, fields validate from native constraints (required, type, pattern) or a custom validate function on the root. Each error can target one condition with match, so the message fits the mistake:

<FieldRoot name="email">
  <FieldLabel>Email</FieldLabel>
  <Input type="email" required />
  <FieldError match="valueMissing">Email is required.</FieldError>
  <FieldError match="typeMismatch">That doesn't look like an email.</FieldError>
</FieldRoot>

Styling states

The field and the control inside both carry the state (data-invalid, data-touched, data-dirty, data-disabled), so custom styling never needs JavaScript:

.ub-field[data-invalid] .ub-field-label {
  color: var(--ub-danger);
}

Props

Each part passes the full Base UI API through. The essentials:

PropTypeDefaultDescription
FieldRoot.namestringThe field's name, forwarded to the control inside.
FieldRoot.invalidbooleanForces the invalid state, for server-side errors.
FieldRoot.disabledbooleanfalseDisables the control and dims the label.
FieldRoot.validationMode"onBlur" | "onChange" | "onSubmit""onSubmit"When validation runs; overrides the Form-level setting for this field.
FieldRoot.validationDebounceTimenumber0Milliseconds to wait before validating on change.
FieldRoot.validate(value, formValues) => string | string[] | null | Promise<...>Custom validation, sync or async; return the error message(s) or null. Receives the other fields' values too.
FieldError.matchboolean | keyof ValidityStateShow this error only for one validity condition; true forces it on.

Agent instructions

Working with a coding agent? Paste this into its context, or point it at llms.txt for the whole library.

FieldRoot, FieldLabel, FieldDescription, FieldError, from @usebones/react.
- Put one Bones control inside FieldRoot (Input, Checkbox, Select, Switch); label wiring, aria-describedby, and validation state are automatic.
- FieldRoot: name, disabled, invalid (for server errors), validate(value).
- FieldError renders only while invalid; match="valueMissing" (or any ValidityState key) binds it to one condition. A forced invalid on the root needs match (true) on the error for it to show.
- Inside a Field, prefer FieldDescription and FieldError over the control's own hint and invalid props.
- Restyle in CSS via .ub-field, .ub-field-label, .ub-field-description, .ub-field-error, and [data-invalid], [data-touched], [data-dirty], [data-filled], [data-disabled] on the root and the control. Tokens only.