form

Field

Wraps one form control with a label, help text and an error message, and wires up the accessibility attributes.

npx blankui add field

We never share it.

When to use

  • Every form control that needs a visible label. That is almost all of them.
  • Showing validation errors from any form library (react-hook-form, TanStack Form, server actions, plain state).

Use something else when

  • A search box in a toolbar with no visible label. Use an Input with aria-label.

Field renders the label, help text and error for one control, and passes id, aria-describedby, aria-invalid and required to the control inside it. Input, Textarea, Select and Checkbox all read this automatically.

Import

import { Field } from "@/components/ui/field"

Example

<form onSubmit={handleSubmit}>
  <Stack gap={4}>
    <Field label="Email" description="We never share your email." error={errors.email} required>
      <Input type="email" name="email" />
    </Field>
    <Field label="Plan">
      <Select name="plan" defaultValue="pro">
        <SelectTrigger>
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="free">Free</SelectItem>
          <SelectItem value="pro">Pro</SelectItem>
        </SelectContent>
      </Select>
    </Field>
    <Field label="I agree to the terms" orientation="horizontal">
      <Checkbox name="terms" />
    </Field>
    <Button type="submit">Create account</Button>
  </Stack>
</form>

With react-hook-form

const { register, formState: { errors } } = useForm<FormValues>()

<Field label="Name" error={errors.name?.message} required>
  <Input {...register("name", { required: "Name is required" })} />
</Field>

Props

  • label (required): visible label text.
  • description: help text under the label.
  • error: error message. Marks the control invalid.
  • required: marks the control required and shows *.
  • orientation: vertical (default) or horizontal. Use horizontal for Checkbox.
  • id: control id. Generated when not set.

Building a new control

Call useFieldControl(props) in your control and spread the result onto the element. It adds the Field wiring and keeps any props you set directly.

Mistakes to avoid

AvoidInstead
<label htmlFor="email">Email</label><Input id="email" /><Field label="Email"><Input /></Field>
{error && <p className="text-red-500">{error}</p>} under an input<Field label="Email" error={error}>
Setting aria-invalid or aria-describedby by handField sets them from the error and description props
Placeholder text instead of a labelA label prop, plus a placeholder only for an example value

Install with the shadcn CLI: npx shadcn add https://blank.vageshwar.dev/r/field.json