Components / Forms

Forms

The form primitives. Field is the orchestrator — it generates the ids and the aria-describedby wiring, and the controls inside it pick that wiring up through context.

Updated 3 days agoEdit on GitHubWeb & native

The pieces

ComponentUse it for
FieldThe wrapper. Generates ids, links label, control, help, and error.
LabelThe control's label. Resolves htmlFor from Field context.
InputA single-line text input.
TextAreaA multi-line text input.
NumberInputAn Input pinned to type="number".
PasswordInputAn Input with an obscure-text toggle.
FieldHelpSupporting copy below the control.
FieldErrorAn error message with role="alert".
FieldsetGroups related fields under a legend.

Field does the wiring

A form control on its own is just an <input>. The accessible relationships — the label points at the control, the control names its help and error text through aria-describedby, the invalid state surfaces as aria-invalid — are what take work.

Field does that work. It generates a stable id, derives a helpId and an errorId from it, and exposes them through React context. Every control inside a Field reads the context and wires itself up: Label resolves its htmlFor, Input claims the id and the aria-describedby list, FieldHelp and FieldError claim their ids. You write the markup; Field connects it.

The controls also work standalone. Drop an <Input> outside a Field and it renders fine — it simply has no context to read, so the wiring is yours to supply.

State flows down

invalid, disabled, and required are Field-level props. Set them on the Field and every control inside inherits them — the Input picks up aria-invalid, the Label renders its required marker, the controls grey out. A control can still override its own state locally; the Field value is a default, not a lock.