Field

Field

Layout primitives that wrap any control with a label, description and error message. Stacks vertically, inline or responsively.

Playground

We'll never share your email.

label
description
invalid
<Field>
  <FieldLabel>Email address</FieldLabel>
  <Input placeholder="you@example.com" />
  <FieldDescription>We'll never share your email.</FieldDescription>
</Field>

Preview

The default composition places the control inside a FieldLabel next to a FieldContent column holding FieldTitle and FieldDescription. Clicking anywhere on the label toggles the control.

<Field orientation="vertical">
  <FieldLabel>
    <Checkbox size="sm" />
    <FieldContent>
      <FieldTitle>Accept terms</FieldTitle>
      <FieldDescription>
        You agree to our terms of service and privacy policy.
      </FieldDescription>
    </FieldContent>
  </FieldLabel>
</Field>

Installation

npx shadcn@latest add https://espresso-base-ui.vercel.app/r/field.json

Usage

import {
  Field,
  FieldContent,
  FieldDescription,
  FieldError,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSeparator,
  FieldSet,
  FieldTitle,
} from "@/components/ui/field"
<FieldGroup>
  <Field>
    <FieldLabel>
      <Checkbox size="sm" />
      <FieldContent>
        <FieldTitle>Marketing emails</FieldTitle>
        <FieldDescription>Receive product updates.</FieldDescription>
      </FieldContent>
    </FieldLabel>
  </Field>
</FieldGroup>

Choice cards

Invert the nesting – a Field inside a FieldLabel – and the label becomes a padded, hoverable card. Group radio cards under a FieldSet with a FieldLegend.

Compute Environment

Select the compute environment for your cluster.

<FieldSet>
  <FieldLegend variant="label">Compute Environment</FieldLegend>
  <FieldDescription>
    Select the compute environment for your cluster.
  </FieldDescription>
  <RadioGroup defaultValue="kubernetes">
    <FieldLabel htmlFor="kubernetes">
      <Field orientation="horizontal">
        <RadioGroupItem value="kubernetes" id="kubernetes" />
        <FieldContent>
          <FieldTitle>Kubernetes</FieldTitle>
          <FieldDescription>
            Run GPU workloads on a K8s cluster.
          </FieldDescription>
        </FieldContent>
      </Field>
    </FieldLabel>
    <FieldLabel htmlFor="vm">
      <Field orientation="horizontal">
        <RadioGroupItem value="vm" id="vm" />
        <FieldContent>
          <FieldTitle>Virtual Machine</FieldTitle>
          <FieldDescription>
            Access a cluster to run GPU workloads.
          </FieldDescription>
        </FieldContent>
      </Field>
    </FieldLabel>
  </RadioGroup>
</FieldSet>

Horizontal orientation

orientation="horizontal" lays the label and control out in a row – the classic settings-page switch. Use responsive to stack on narrow containers and go horizontal from the @md container breakpoint.

<FieldGroup>
  <Field orientation="horizontal">
    <FieldLabel>
      <Switch size="sm" />
      <FieldContent>
        <FieldTitle>Dark mode</FieldTitle>
        <FieldDescription>Use dark theme across the app.</FieldDescription>
      </FieldContent>
    </FieldLabel>
  </Field>
  <FieldSeparator />
  <Field orientation="horizontal">
    <FieldLabel>
      <Switch size="sm" defaultChecked />
      <FieldContent>
        <FieldTitle>Compact mode</FieldTitle>
        <FieldDescription>Reduce spacing in the UI.</FieldDescription>
      </FieldContent>
    </FieldLabel>
  </Field>
</FieldGroup>

Invalid and disabled

Set data-invalid="true" on the Field to tint it destructive and render a FieldError; data-disabled="true" dims the label and blocks pointer events on choice cards.

<Field orientation="vertical" data-invalid="true">
  <FieldLabel>
    <Checkbox size="sm" />
    <FieldContent>
      <FieldTitle>Accept terms</FieldTitle>
      <FieldDescription>
        You must accept the terms to continue.
      </FieldDescription>
      <FieldError>This field is required.</FieldError>
    </FieldContent>
  </FieldLabel>
</Field>

<Field orientation="vertical" data-disabled="true">
  <FieldLabel>
    <Checkbox size="sm" disabled />
    <FieldContent>
      <FieldTitle>Disabled option</FieldTitle>
      <FieldDescription>
        This option is currently unavailable.
      </FieldDescription>
    </FieldContent>
  </FieldLabel>
</Field>

API reference

All parts render plain elements (div, label, fieldset, legend, p) and accept className plus the standard props of their element. Only the parts below take extra props.

Field

PropTypeDefaultDescription
orientation"vertical" | "horizontal" | "responsive""vertical"Stacks label and control (vertical), lays them out in a row (horizontal), or stacks then switches to a row at the @md container breakpoint (responsive). Reflected as data-orientation.

FieldLegend

PropTypeDefaultDescription
variant"legend" | "label""legend"Typography of the legend – text-base for a section legend, text-sm to match a field label. Reflected as data-variant.

FieldError

PropTypeDefaultDescription
errorsArray<{ message?: string } | undefined>–Error objects (e.g. from react-hook-form). One message renders inline; multiple unique messages render as a bulleted list. Ignored when children are passed; renders nothing when empty.
ComponentDescription
FieldSetNative fieldset wrapper for a group of related fields (data-slot="field-set").
FieldLegendNative legend for a FieldSet (data-slot="field-legend").
FieldGroupVertical stack of fields and a container-query context for the responsive orientation (data-slot="field-group").
FieldOne field row with role="group" (data-slot="field"). Styling reacts to data-invalid and data-disabled.
FieldLabelLabel built on the Label component (data-slot="field-label"). Wrapping a Field turns it into an interactive choice card with hover, active and focus-within surfaces.
FieldContentColumn that groups title, description and error next to the control (data-slot="field-content").
FieldTitleTitle line in label typography (shares data-slot="field-label") for use inside FieldContent.
FieldDescriptionMuted supporting copy (data-slot="field-description"). Links inside are underlined.
FieldSeparatorHorizontal rule between fields (data-slot="field-separator"), with optional inline content such as "or".
FieldErrorDestructive-colored message with role="alert" (data-slot="field-error").

Accessibility & styling hooks

Field renders role="group" and FieldError renders role="alert" so validation messages are announced. FieldLabel is a real label – nest the control inside it or pass htmlFor pointing at the control's id (as in the radio choice cards) for label association.

State flows through data attributes rather than context: data-invalid="true" on a Field turns the whole group destructive (and tints inputs inside it), and data-disabled="true" fades the label. Every part exposes its data-slot attribute for CSS targeting, and Field reflects data-orientation.

Search documentation

Search the component docs