Components

The label + control + help/error wrapper that every form row is built from. It generates an id, associates the <label>, wires aria-describedby to the description/error, and flips the control into its invalid state when error is set, so any control that accepts id/aria-* (Input, Textarea, Select) gets accessible labelling for free. label/description/error are data props; the control is a composition slot.

Install

npx shadcn@latest add @alix/field

Preview

Internal code-name.

Usage

<Field label="Engagement name" description="Internal code-name." required>
  <Input placeholder="e.g. Project Atlas" />
</Field>

<Field label="Owner" error="Select an owner.">
  <Select items={owners} placeholder="Unassigned" />
</Field>

Examples

01

Select with helper text

Field wires the label and description to Select just as it does for a text input.

Shown in the engagement dashboard.

<Field label="Owner" description="Shown in the engagement dashboard.">
  <Select
    placeholder="Unassigned"
    items={[
      { value: "jp", label: "J. Patel" },
      { value: "rs", label: "R. Silva" },
      { value: "mk", label: "M. Khan" },
    ]}
  />
</Field>
02

Error replaces help

When error is present, the helper copy is hidden and the child control receives aria-invalid.

Client code is required.

<Field
  label="Client code"
  description="Use the finance system identifier."
  error="Client code is required."
  required
>
  <Input placeholder="e.g. ALX-1042" />
</Field>
03

Form section

A form section composes Field with Input, Select, Textarea, and Button without Field mirroring any control API.

<div className="flex flex-col gap-4">
  <div className="grid gap-4 md:grid-cols-2">
    <Field label="Engagement name" required>
      <Input defaultValue="Project Atlas" />
    </Field>
    <Field label="Priority">
      <Select
        defaultValue="urgent"
        items={[
          { value: "urgent", label: "Urgent" },
          { value: "standard", label: "Standard" },
        ]}
      />
    </Field>
  </div>
  <Field label="Decision needed">
    <Textarea rows={4} placeholder="Summarise the client decision." />
  </Field>
  <div>
    <Button>Save request</Button>
  </div>
</div>

When to use it

Use it for

  • Every labelled form row that needs accessible label, description, required, or error wiring.
  • Input, Textarea, Select, or another single control that accepts id and aria attributes.
  • Validation states where the control and error message must stay associated.

Reach for something else

  • Plain layout groups with no form control: use Stack or Grid instead.
  • Standalone check rows with visible label text beside the box: use Checkbox with a label instead.
  • Inline add-ons around a control: use InputGroup inside an already-labelled context instead.

Props

PropTypeDefaultDescription
labelReactNodeRendered as a <label> wired to the control via htmlFor.
descriptionReactNodeHelper text under the control. Hidden while an error is showing.
errorReactNodeError message. Turns the control's invalid treatment on and replaces the description.
requiredbooleanfalseAdds an asterisk and forwards required to the control.
childrenReact.ReactElementThe single form control: a composition slot, not a config. Field clones it to inject id, aria-describedby, aria-invalid, and required.

See also