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.
<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.jsonUsage
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.
<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
| Prop | Type | Default | Description |
|---|---|---|---|
| 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
| Prop | Type | Default | Description |
|---|---|---|---|
| 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
| Prop | Type | Default | Description |
|---|---|---|---|
| errors | Array<{ 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. |
| Component | Description |
|---|---|
| FieldSet | Native fieldset wrapper for a group of related fields (data-slot="field-set"). |
| FieldLegend | Native legend for a FieldSet (data-slot="field-legend"). |
| FieldGroup | Vertical stack of fields and a container-query context for the responsive orientation (data-slot="field-group"). |
| Field | One field row with role="group" (data-slot="field"). Styling reacts to data-invalid and data-disabled. |
| FieldLabel | Label 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. |
| FieldContent | Column that groups title, description and error next to the control (data-slot="field-content"). |
| FieldTitle | Title line in label typography (shares data-slot="field-label") for use inside FieldContent. |
| FieldDescription | Muted supporting copy (data-slot="field-description"). Links inside are underlined. |
| FieldSeparator | Horizontal rule between fields (data-slot="field-separator"), with optional inline content such as "or". |
| FieldError | Destructive-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.