---
import { Checkbox } from "@/components/ui/checkbox"
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
---
<FieldSet>
<FieldLegend>Profile</FieldLegend>
<FieldDescription>Enter your profile information below.</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel for="name">Full name</FieldLabel>
<Input type="text" id="name" placeholder="Evil Rabbit" />
<FieldDescription>This appears on invoices and emails.</FieldDescription>
</Field>
<Field>
<FieldLabel for="username">Username</FieldLabel>
<Input type="text" id="username" placeholder="evilrabbit" />
</Field>
<Field orientation="horizontal">
<Checkbox id="newsletter" />
<FieldLabel for="newsletter">Subscribe to the newsletter</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>Installation
npx shadcn@latest add @fulldev/field
Usage
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
FieldTitle,
} from "@/components/ui/field"
<FieldSet>
<FieldLegend>Profile</FieldLegend>
<FieldGroup>
<Field>
<FieldLabel for="name">Full name</FieldLabel>
<Input id="name" placeholder="Evil Rabbit" />
<FieldDescription>Optional helper text.</FieldDescription>
</Field>
</FieldGroup>
</FieldSet>
Composition
Use the following composition for a single field with a label, control, helper text, and validation:
Field
├── FieldLabel
├── Input / Textarea / Switch / Select
├── FieldDescription
└── FieldError
Use FieldGroup to group related fields. Add FieldSeparator between sections when needed.
FieldGroup
├── Field
│ ├── FieldLabel
│ ├── Input / Textarea / Switch / Select
│ ├── FieldDescription
│ └── FieldError
├── FieldSeparator
└── Field
├── FieldLabel
└── Input / Textarea / Switch / Select
Use FieldSet and FieldLegend for semantic grouping:
FieldSet
├── FieldLegend
├── FieldDescription
└── FieldGroup
├── Field
│ ├── FieldLabel
│ ├── Input / Textarea / Switch / Select
│ ├── FieldDescription
│ └── FieldError
└── Field
├── FieldLabel
└── Input / Textarea / Switch / Select
Anatomy
The Field family is designed for composing accessible forms. A typical field is structured as follows:
<Field>
<FieldLabel for="input-id">Label</FieldLabel>
<!-- Input, Select, Checkbox, etc. -->
<FieldDescription>Optional helper text.</FieldDescription>
</Field>
Fieldis the core wrapper for a single field.FieldContentis a flex column that groups label and description. Not required if you have no description.- Wrap related fields with
FieldGroup, and useFieldSetwithFieldLegendfor semantic grouping.
Examples
We'll never share your email.
---
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
---
<div class="w-full max-w-sm">
<Field>
<FieldLabel for="email">Email</FieldLabel>
<Input id="email" type="email" placeholder="email@example.com" />
<FieldDescription>We'll never share your email.</FieldDescription>
</Field>
</div>Write a short bio about yourself.
---
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { Textarea } from "@/components/ui/textarea"
---
<div class="w-full max-w-sm">
<Field>
<FieldLabel for="bio">Bio</FieldLabel>
<Textarea id="bio" placeholder="Tell us about yourself" />
<FieldDescription>Write a short bio about yourself.</FieldDescription>
</Field>
</div>Select your country of residence.
---
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select"
---
<div class="w-full max-w-sm">
<Field>
<FieldLabel for="country">Country</FieldLabel>
<NativeSelect id="country">
<NativeSelectOption value="">Select a country</NativeSelectOption>
<NativeSelectOption value="us">United States</NativeSelectOption>
<NativeSelectOption value="uk">United Kingdom</NativeSelectOption>
<NativeSelectOption value="ca">Canada</NativeSelectOption>
</NativeSelect>
<FieldDescription>Select your country of residence.</FieldDescription>
</Field>
</div>---
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
---
<div class="w-full max-w-sm">
<FieldSet>
<FieldLegend>Personal Information</FieldLegend>
<FieldDescription>Enter your personal details below.</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel for="first-name">First name</FieldLabel>
<Input id="first-name" placeholder="John" />
</Field>
<Field>
<FieldLabel for="last-name">Last name</FieldLabel>
<Input id="last-name" placeholder="Doe" />
</Field>
</FieldGroup>
</FieldSet>
</div>You agree to our Terms of Service and Privacy Policy.
---
import { Checkbox } from "@/components/ui/checkbox"
import {
Field,
FieldContent,
FieldDescription,
FieldLabel,
} from "@/components/ui/field"
---
<div class="w-full max-w-sm">
<Field orientation="horizontal">
<Checkbox id="terms" />
<FieldContent>
<FieldLabel for="terms">Accept terms and conditions</FieldLabel>
<FieldDescription>
You agree to our Terms of Service and Privacy Policy.
</FieldDescription>
</FieldContent>
</Field>
</div>---
import {
Field,
FieldDescription,
FieldLabel,
FieldLegend,
FieldSet,
} from "@/components/ui/field"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
---
<div class="w-full max-w-sm">
<FieldSet>
<FieldLegend>Notifications</FieldLegend>
<FieldDescription>Choose how you want to be notified.</FieldDescription>
<RadioGroup defaultValue="email" name="notifications">
<Field orientation="horizontal">
<RadioGroupItem value="email" id="email" />
<FieldLabel for="email">Email</FieldLabel>
</Field>
<Field orientation="horizontal">
<RadioGroupItem value="sms" id="sms" />
<FieldLabel for="sms">SMS</FieldLabel>
</Field>
<Field orientation="horizontal">
<RadioGroupItem value="push" id="push" />
<FieldLabel for="push">Push notification</FieldLabel>
</Field>
</RadioGroup>
</FieldSet>
</div>Field Group
Stack Field components with FieldGroup. Add FieldSeparator to divide them.
Your primary email address.
Your phone number for SMS updates.
---
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldSeparator,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
---
<div class="w-full max-w-sm">
<FieldGroup>
<Field>
<FieldLabel for="email-group">Email</FieldLabel>
<Input id="email-group" type="email" placeholder="email@example.com" />
<FieldDescription>Your primary email address.</FieldDescription>
</Field>
<FieldSeparator>Or</FieldSeparator>
<Field>
<FieldLabel for="phone">Phone</FieldLabel>
<Input id="phone" type="tel" placeholder="+1 (555) 000-0000" />
<FieldDescription>Your phone number for SMS updates.</FieldDescription>
</Field>
</FieldGroup>
</div>Responsive Layout
- Vertical fields: Default orientation stacks label, control, and helper text—ideal for mobile-first layouts.
- Horizontal fields: Set
orientation="horizontal"onFieldto align the label and control side-by-side. Pair withFieldContentto keep descriptions aligned. - Responsive fields: Set
orientation="responsive"for automatic column layouts inside container-aware parents. Apply@container/field-groupclasses onFieldGroupto switch orientations at specific breakpoints.
Help improve the app by sharing anonymous usage data.
Receive emails about new products and features.
---
import { Checkbox } from "@/components/ui/checkbox"
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
} from "@/components/ui/field"
---
<div class="w-full max-w-2xl">
<FieldGroup class="@container/field-group">
<Field orientation="responsive">
<Checkbox id="responsive-checkbox" />
<FieldContent>
<FieldLabel for="responsive-checkbox">
Analytics and performance
</FieldLabel>
<FieldDescription>
Help improve the app by sharing anonymous usage data.
</FieldDescription>
</FieldContent>
</Field>
<Field orientation="responsive">
<Checkbox id="responsive-marketing" />
<FieldContent>
<FieldLabel for="responsive-marketing">Marketing emails</FieldLabel>
<FieldDescription>
Receive emails about new products and features.
</FieldDescription>
</FieldContent>
</Field>
</FieldGroup>
</div>Validation
Add data-invalid="true" to Field to switch the entire block into an error state, and add aria-invalid="true" on the input itself for assistive technologies.
Enter a valid email address.
This email will be used for account notifications.
---
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
---
<div class="w-full max-w-sm">
<Field data-invalid="true">
<FieldLabel for="error-email">Email</FieldLabel>
<Input id="error-email" type="email" aria-invalid="true" />
<FieldDescription class="text-destructive">
Enter a valid email address.
</FieldDescription>
<FieldDescription
>This email will be used for account notifications.</FieldDescription
>
</Field>
</div>Accessibility
FieldSetandFieldLegendkeep related controls grouped for keyboard and assistive tech users.Fieldoutputsrole="group"so nested controls inherit labeling fromFieldLabelandFieldLegendwhen combined.- Apply
FieldSeparatorsparingly to ensure screen readers encounter clear section boundaries.
API Reference
See the GitHub source code for more information on props.