Forms

FormField

Binds a label, helper text and error to a control.

Client component1 export

The value here is the wiring, not the layout. FormField generates stable ids with `useId` and connects them through `htmlFor` and `aria-describedby` — the part hand-written forms almost always get wrong. Anything you set explicitly on the child wins.

Import

import { FormField } from "@abbainitiative/ui";

Also available from the subpath entry @abbainitiative/ui/form-field.

This component carries its own "use client" directive. You can still render it from a Server Component — you simply cannot pass it a function prop, because functions do not serialise across the boundary.

Examples

Label, description and error

Focus the field with a screen reader running: the label, then the description, then the error are announced in order.

We only use this to sign you in.

<Stack gap={5}>
  <FormField label="Email address" description="We only use this to sign you in." required>
    <Input type="email" placeholder="you@example.com" />
  </FormField>

  <FormField label="Username" error="That username is already taken.">
    <Input defaultValue="ada" />
  </FormField>
</Stack>

Props

FormField props. All native attributes of the underlying element are also accepted and forwarded.
PropTypeDefaultDescription
labelReactNodeVisible caption for the control.
descriptionReactNodeHelper text describing the expected input.
errorReactNodeValidation failure. Its presence puts the control into the invalid state.
requiredbooleanfalseMarks the control required, visually and in its props.
disabledbooleanfalseDims the label and disables the control.
children (required)ReactElementExactly one form control.

Accessibility

  • `htmlFor` and the control's `id` are generated together, so clicking the label focuses the field.
  • Description and error ids are joined into `aria-describedby` in reading order.
  • An error sets `aria-invalid` on the control and renders the message with `role="alert"`.
  • Ids come from `useId`, which is stable across server and client, so the association survives hydration.
  • An explicit `id` or `aria-describedby` on the child is never overwritten.