Field
Layout wrapper that composes label, description, error, and control in vertical or horizontal orientation.
Examples
Checkbox
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.
import { Checkbox } from "@codefast/ui/checkbox";
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@codefast/ui/field";
export function FieldCheckbox() {
return (
<FieldGroup className="w-full max-w-xs">
<FieldSet>
<FieldLegend variant="label">Show these items on the desktop</FieldLegend>
<FieldDescription>Select the items you want to show on the desktop.</FieldDescription>
<FieldGroup className="gap-3">
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-hard-disks-ljj" />
<FieldLabel htmlFor="finder-pref-9k2-hard-disks-ljj" className="font-normal" defaultChecked>
Hard disks
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-external-disks-1yg" />
<FieldLabel htmlFor="finder-pref-9k2-external-disks-1yg" className="font-normal">
External disks
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-cds-dvds-fzt" />
<FieldLabel htmlFor="finder-pref-9k2-cds-dvds-fzt" className="font-normal">
CDs, DVDs, and iPods
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-connected-servers-6l2" />
<FieldLabel htmlFor="finder-pref-9k2-connected-servers-6l2" className="font-normal">
Connected servers
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
<FieldSeparator />
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-sync-folders-nep" defaultChecked />
<FieldContent>
<FieldLabel htmlFor="finder-pref-9k2-sync-folders-nep">Sync Desktop & Documents folders</FieldLabel>
<FieldDescription>
Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.
</FieldDescription>
</FieldContent>
</Field>
</FieldGroup>
);
}
Choice Card
Wrap Field components inside FieldLabel to create selectable field groups. This works with RadioItem, Checkbox and Switch components.
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
FieldTitle,
} from "@codefast/ui/field";
import { RadioGroup, RadioGroupItem } from "@codefast/ui/radio-group";
export function FieldChoiceCard() {
return (
<FieldGroup className="w-full max-w-xs">
<FieldSet>
<FieldLegend variant="label">Compute Environment</FieldLegend>
<FieldDescription>Select the compute environment for your cluster.</FieldDescription>
<RadioGroup defaultValue="kubernetes">
<FieldLabel htmlFor="kubernetes-r2h">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Kubernetes</FieldTitle>
<FieldDescription>Run GPU workloads on a K8s cluster.</FieldDescription>
</FieldContent>
<RadioGroupItem value="kubernetes" id="kubernetes-r2h" />
</Field>
</FieldLabel>
<FieldLabel htmlFor="vm-z4k">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Virtual Machine</FieldTitle>
<FieldDescription>Access a cluster to run GPU workloads.</FieldDescription>
</FieldContent>
<RadioGroupItem value="vm" id="vm-z4k" />
</Field>
</FieldLabel>
</RadioGroup>
</FieldSet>
</FieldGroup>
);
}
Fieldset
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSet } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function FieldFieldset() {
return (
<FieldSet className="w-full max-w-sm">
<FieldLegend>Address Information</FieldLegend>
<FieldDescription>We need your address to deliver your order.</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel htmlFor="street">Street Address</FieldLabel>
<Input id="street" type="text" placeholder="123 Main St" />
</Field>
<div className="grid grid-cols-2 gap-4">
<Field>
<FieldLabel htmlFor="city">City</FieldLabel>
<Input id="city" type="text" placeholder="New York" />
</Field>
<Field>
<FieldLabel htmlFor="zip">Postal Code</FieldLabel>
<Input id="zip" type="text" placeholder="90502" />
</Field>
</div>
</FieldGroup>
</FieldSet>
);
}
Field Group
Stack Field components with FieldGroup. Add FieldSeparator to divide them.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldSeparator, FieldSet } from "@codefast/ui/field";
export function FieldGroupExample() {
return (
<FieldGroup className="w-full max-w-xs">
<FieldSet>
<FieldLabel>Responses</FieldLabel>
<FieldDescription>
Get notified when ChatGPT responds to requests that take time, like research or image generation.
</FieldDescription>
<FieldGroup data-slot="checkbox-group">
<Field orientation="horizontal">
<Checkbox id="push" defaultChecked disabled />
<FieldLabel htmlFor="push" className="font-normal">
Push notifications
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
<FieldSeparator />
<FieldSet>
<FieldLabel>Tasks</FieldLabel>
<FieldDescription>
Get notified when tasks you've created have updates. <a href="/">Manage tasks</a>
</FieldDescription>
<FieldGroup data-slot="checkbox-group">
<Field orientation="horizontal">
<Checkbox id="push-tasks" />
<FieldLabel htmlFor="push-tasks" className="font-normal">
Push notifications
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="email-tasks" />
<FieldLabel htmlFor="email-tasks" className="font-normal">
Email notifications
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
</FieldGroup>
);
}
Input
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldSet } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function FieldInput() {
return (
<FieldSet className="w-full max-w-xs">
<FieldGroup>
<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input id="username" type="text" placeholder="Leo Park" />
<FieldDescription>Choose a unique username for your account.</FieldDescription>
</Field>
<Field>
<FieldLabel htmlFor="password">Password</FieldLabel>
<FieldDescription>Must be at least 8 characters long.</FieldDescription>
<Input id="password" type="password" placeholder="••••••••" />
</Field>
</FieldGroup>
</FieldSet>
);
}
Radio
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
import { Field, FieldDescription, FieldLabel, FieldLegend, FieldSet } from "@codefast/ui/field";
import { RadioGroup, RadioGroupItem } from "@codefast/ui/radio-group";
export function FieldRadio() {
return (
<FieldSet className="w-full max-w-xs">
<FieldLegend variant="label">Subscription Plan</FieldLegend>
<FieldDescription>Yearly and lifetime plans offer significant savings.</FieldDescription>
<RadioGroup defaultValue="monthly">
<Field orientation="horizontal">
<RadioGroupItem value="monthly" id="plan-monthly" />
<FieldLabel htmlFor="plan-monthly" className="font-normal">
Monthly ($9.99/month)
</FieldLabel>
</Field>
<Field orientation="horizontal">
<RadioGroupItem value="yearly" id="plan-yearly" />
<FieldLabel htmlFor="plan-yearly" className="font-normal">
Yearly ($99.99/year)
</FieldLabel>
</Field>
<Field orientation="horizontal">
<RadioGroupItem value="lifetime" id="plan-lifetime" />
<FieldLabel htmlFor="plan-lifetime" className="font-normal">
Lifetime ($299.99)
</FieldLabel>
</Field>
</RadioGroup>
</FieldSet>
);
}
Responsive Layout
Field layouts adapt responsively to their container.
import { Button } from "@codefast/ui/button";
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
} from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function FieldResponsive() {
return (
<div className="w-full max-w-lg">
<form>
<FieldSet>
<FieldLegend>Profile</FieldLegend>
<FieldDescription>Fill in your profile information.</FieldDescription>
<FieldGroup>
<Field orientation="responsive">
<FieldContent>
<FieldLabel htmlFor="name">Name</FieldLabel>
<FieldDescription>Provide your full name for identification</FieldDescription>
</FieldContent>
<Input id="name" placeholder="Ava Stone" required />
</Field>
<Field orientation="responsive">
<Button type="submit">Submit</Button>
<Button type="button" variant="outline">
Cancel
</Button>
</Field>
</FieldGroup>
</FieldSet>
</form>
</div>
);
}
RTL
Right-to-left layout support for languages such as Arabic and Hebrew.
Translations are AI-generated for demonstration and may be imperfect.
import { Button } from "@codefast/ui/button";
import { Checkbox } from "@codefast/ui/checkbox";
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
import { Textarea } from "@codefast/ui/textarea";
import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";
const months = [
{ label: "MM", value: null },
{ label: "01", value: "01" },
{ label: "02", value: "02" },
{ label: "03", value: "03" },
{ label: "04", value: "04" },
{ label: "05", value: "05" },
{ label: "06", value: "06" },
{ label: "07", value: "07" },
{ label: "08", value: "08" },
{ label: "09", value: "09" },
{ label: "10", value: "10" },
{ label: "11", value: "11" },
{ label: "12", value: "12" },
];
const years = [
{ label: "YYYY", value: null },
{ label: "2024", value: "2024" },
{ label: "2025", value: "2025" },
{ label: "2026", value: "2026" },
{ label: "2027", value: "2027" },
{ label: "2028", value: "2028" },
{ label: "2029", value: "2029" },
];
const translations: Translations = {
en: {
dir: "ltr",
values: {
paymentMethod: "Payment Method",
secureTransactions: "All transactions are secure and encrypted",
nameOnCard: "Name on Card",
cardNumber: "Card Number",
cardNumberDescription: "Enter your 16-digit card number",
month: "Month",
year: "Year",
cvv: "CVV",
monthPlaceholder: "MM",
month01: "01",
month02: "02",
month03: "03",
month04: "04",
month05: "05",
month06: "06",
month07: "07",
month08: "08",
month09: "09",
month10: "10",
month11: "11",
month12: "12",
billingAddress: "Billing Address",
billingAddressDescription: "The billing address associated with your payment method",
sameAsShipping: "Same as shipping address",
comments: "Comments",
commentsPlaceholder: "Add any additional comments",
submit: "Submit",
cancel: "Cancel",
},
},
ar: {
dir: "rtl",
values: {
paymentMethod: "طريقة الدفع",
secureTransactions: "جميع المعاملات آمنة ومشفرة",
nameOnCard: "الاسم على البطاقة",
cardNumber: "رقم البطاقة",
cardNumberDescription: "أدخل رقم البطاقة المكون من 16 رقمًا",
month: "الشهر",
year: "السنة",
cvv: "CVV",
monthPlaceholder: "ش.ش",
month01: "٠١",
month02: "٠٢",
month03: "٠٣",
month04: "٠٤",
month05: "٠٥",
month06: "٠٦",
month07: "٠٧",
month08: "٠٨",
month09: "٠٩",
month10: "١٠",
month11: "١١",
month12: "١٢",
billingAddress: "عنوان الفوترة",
billingAddressDescription: "عنوان الفوترة المرتبط بطريقة الدفع الخاصة بك",
sameAsShipping: "نفس عنوان الشحن",
comments: "تعليقات",
commentsPlaceholder: "أضف أي تعليقات إضافية",
submit: "إرسال",
cancel: "إلغاء",
},
},
he: {
dir: "rtl",
values: {
paymentMethod: "אמצעי תשלום",
secureTransactions: "כל העסקאות מאובטחות ומוצפנות",
nameOnCard: "שם על הכרטיס",
cardNumber: "מספר כרטיס",
cardNumberDescription: "הזן את מספר הכרטיס בן 16 הספרות שלך",
month: "חודש",
year: "שנה",
cvv: "CVV",
monthPlaceholder: "MM",
month01: "01",
month02: "02",
month03: "03",
month04: "04",
month05: "05",
month06: "06",
month07: "07",
month08: "08",
month09: "09",
month10: "10",
month11: "11",
month12: "12",
billingAddress: "כתובת חיוב",
billingAddressDescription: "כתובת החיוב המשויכת לאמצעי התשלום שלך",
sameAsShipping: "זהה לכתובת המשלוח",
comments: "הערות",
commentsPlaceholder: "הוסף הערות נוספות",
submit: "שלח",
cancel: "בטל",
},
},
};
export function FieldRtl() {
const { dir, t } = useTranslation(translations, "ar");
const getMonthLabel = (value: string | null): string => {
if (value === null) {
return t.monthPlaceholder ?? "";
}
const monthKey = `month${value}` as keyof typeof t;
return t[monthKey] || value;
};
return (
<div className="w-full max-w-md py-6" dir={dir}>
<form>
<FieldGroup>
<FieldSet>
<FieldLegend>{t.paymentMethod}</FieldLegend>
<FieldDescription>{t.secureTransactions}</FieldDescription>
<FieldGroup>
<Field>
<FieldLabel htmlFor="checkout-7j9-card-name-43j-rtl">{t.nameOnCard}</FieldLabel>
<Input id="checkout-7j9-card-name-43j-rtl" placeholder="Ava Stone" required />
</Field>
<Field>
<FieldLabel htmlFor="checkout-7j9-card-number-uw1-rtl">{t.cardNumber}</FieldLabel>
<Input id="checkout-7j9-card-number-uw1-rtl" placeholder="1234 5678 9012 3456" required />
<FieldDescription>{t.cardNumberDescription}</FieldDescription>
</Field>
<div className="grid grid-cols-3 gap-4">
<Field>
<FieldLabel htmlFor="checkout-exp-month-ts6-rtl">{t.month}</FieldLabel>
<Select>
<SelectTrigger id="checkout-exp-month-ts6-rtl">
<SelectValue />
</SelectTrigger>
<SelectContent dir={dir}>
<SelectGroup>
{months.map((item) => (
<SelectItem key={item.label} value={item.value ?? item.label}>
{getMonthLabel(item.value)}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</Field>
<Field>
<FieldLabel htmlFor="checkout-7j9-exp-year-f59-rtl">{t.year}</FieldLabel>
<Select>
<SelectTrigger id="checkout-7j9-exp-year-f59-rtl">
<SelectValue />
</SelectTrigger>
<SelectContent dir={dir}>
<SelectGroup>
{years.map((item) => (
<SelectItem key={item.label} value={item.value ?? item.label}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</Field>
<Field>
<FieldLabel htmlFor="checkout-7j9-cvv-rtl">{t.cvv}</FieldLabel>
<Input id="checkout-7j9-cvv-rtl" placeholder="123" required />
</Field>
</div>
</FieldGroup>
</FieldSet>
<FieldSeparator />
<FieldSet>
<FieldLegend>{t.billingAddress}</FieldLegend>
<FieldDescription>{t.billingAddressDescription}</FieldDescription>
<FieldGroup>
<Field orientation="horizontal">
<Checkbox id="checkout-7j9-same-as-shipping-wgm-rtl" defaultChecked />
<FieldLabel htmlFor="checkout-7j9-same-as-shipping-wgm-rtl" className="font-normal">
{t.sameAsShipping}
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
<FieldSet>
<FieldGroup>
<Field>
<FieldLabel htmlFor="checkout-7j9-optional-comments-rtl">{t.comments}</FieldLabel>
<Textarea
id="checkout-7j9-optional-comments-rtl"
placeholder={t.commentsPlaceholder}
className="resize-none"
/>
</Field>
</FieldGroup>
</FieldSet>
<Field orientation="horizontal">
<Button type="submit">{t.submit}</Button>
<Button variant="outline" type="button">
{t.cancel}
</Button>
</Field>
</FieldGroup>
</form>
</div>
);
}
Select
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
Select your department or area of work.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
export function FieldSelect() {
return (
<Field className="w-full max-w-xs">
<FieldLabel>Department</FieldLabel>
<Select>
<SelectTrigger>
<SelectValue placeholder="Choose department" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="engineering">Engineering</SelectItem>
<SelectItem value="design">Design</SelectItem>
<SelectItem value="marketing">Marketing</SelectItem>
<SelectItem value="sales">Sales</SelectItem>
<SelectItem value="support">Customer Support</SelectItem>
<SelectItem value="hr">Human Resources</SelectItem>
<SelectItem value="finance">Finance</SelectItem>
<SelectItem value="operations">Operations</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FieldDescription>Select your department or area of work.</FieldDescription>
</Field>
);
}
Slider
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
Set your budget range ($200 - 800).
import { Field, FieldDescription, FieldTitle } from "@codefast/ui/field";
import { Slider } from "@codefast/ui/slider";
import * as React from "react";
export function FieldSlider() {
const [value, setValue] = React.useState([200, 800]);
return (
<Field className="w-full max-w-xs">
<FieldTitle>Price Range</FieldTitle>
<FieldDescription>
Set your budget range ($
<span className="font-medium tabular-nums">{value[0]}</span> -{" "}
<span className="font-medium tabular-nums">{value[1]}</span>).
</FieldDescription>
<Slider
value={value}
onValueChange={(value) => setValue(value as [number, number])}
max={1000}
min={0}
step={10}
className="mt-2 w-full"
aria-label="Price Range"
/>
</Field>
);
}
Switch
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
import { Field, FieldLabel } from "@codefast/ui/field";
import { Switch } from "@codefast/ui/switch";
export function FieldSwitch() {
return (
<Field orientation="horizontal" className="w-fit">
<FieldLabel htmlFor="2fa">Multi-factor authentication</FieldLabel>
<Switch id="2fa" />
</Field>
);
}
Textarea
Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldSet } from "@codefast/ui/field";
import { Textarea } from "@codefast/ui/textarea";
export function FieldTextarea() {
return (
<FieldSet className="w-full max-w-xs">
<FieldGroup>
<Field>
<FieldLabel htmlFor="feedback">Feedback</FieldLabel>
<Textarea id="feedback" placeholder="Your feedback helps us improve..." rows={4} />
<FieldDescription>Share your thoughts about our service.</FieldDescription>
</Field>
</FieldGroup>
</FieldSet>
);
}
Usage
The minimal import and composition — see Examples below for styled, real-world variants.
import { Field, FieldDescription, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
export function FieldUsage() {
return (
<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input id="username" placeholder="Leo Park" />
<FieldDescription>Choose a unique username for your account.</FieldDescription>
</Field>
);
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- FieldError dedupes repeated error messages and renders a single line or a bullet list automatically — pass an array of { message } straight from a validation library.
- Wrapping an entire Field inside FieldLabel turns it into a clickable choice-card — no extra markup needed, the has-data-checked: styling kicks in on its own.
- FieldSeparator draws a divider with an optional centered label, e.g. "OR".
- orientation="horizontal" lays the control beside the label instead of underneath.
API reference
Props for each part of the component. All native element props are also forwarded.
Field
Vertical wrapper that lays out a label, control, description, and error.
orientation"vertical" | "horizontal"Stack the control under the label, or beside it.
Default
"vertical"
FieldLabel
Accessible label for the field's control.
htmlForstringPoints at the control id for an accessible name.
FieldDescription
Supporting help text under the label.
childrenReactNodeThe supporting copy.
FieldError
Validation message, rendered conditionally when the field is invalid.
errorsArray<{ message?: string } | undefined>Validation errors straight from a form library — deduped and rendered as one line, or a bullet list when there’s more than one.
childrenReactNodeCustom error content — takes priority over errors when provided.
FieldSet
Groups several related fields, like a native fieldset.
childrenReactNodeFieldLegend plus the grouped Field or FieldGroup elements.
FieldLegend
Titles a FieldSet.
variant"legend" | "label"Size the legend like a section title, or "label" for a smaller, label-sized heading.
Default
"legend"childrenReactNodeThe legend text.
FieldGroup
Spaces a set of Field elements evenly.
childrenReactNodeThe Field (or nested FieldGroup) elements to space out.
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
- FieldLabel forwards htmlFor — always link it to the control’s id.
- Set aria-invalid on the control and render FieldError so the error is announced.
- FieldError sets role="alert", so screen readers announce it as soon as it mounts — no extra wiring needed.
- Use FieldSet + FieldLegend for groups of checkboxes or radios.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Pair every control with a FieldLabel.
- Show errors next to the field, on submit or on blur — not only as a toast.
Don’t
- Don’t rely on the red ring alone — always give a text error.
- Don’t use placeholder text as the label.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.