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