- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- CommandNew
- Dialog
- Empty
- Field
- Fieldset
- Form
- Frame
- Group
- Input
- Input Group
- Kbd
- Label
- Menu
- Meter
- Number Field
- Pagination
- Popover
- Preview Card
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Toolbar
- Tooltip
Field
A component that provides labelling and validation for form controls.
Visible on your profile
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
export default function Particle() {
return (
<Field>
<FieldLabel>Name</FieldLabel>
<Input placeholder="Enter your name" type="text" />
<FieldDescription>Visible on your profile</FieldDescription>
</Field>
);
}
Installation
pnpm dlx shadcn@latest add @coss/field
Usage
import {
Field,
FieldDescription,
FieldError,
FieldLabel,
FieldValidity,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"<Field>
<FieldLabel>Name</FieldLabel>
<Input type="text" placeholder="Enter your name" />
<FieldDescription>Visible on your profile</FieldDescription>
<FieldError>Please enter a valid name</FieldError>
<FieldValidity>
{(validity) => (
{validity.error && <p>{validity.error}</p>}
)}
</FieldValidity>
</Field>API Reference
Field
Root component. Styled wrapper for Field.Root from Base UI with flex column layout.
FieldLabel
Label for the field. Styled wrapper for Field.Label from Base UI.
FieldItem
Container for field items. Styled wrapper for Field.Item from Base UI.
FieldDescription
Description text for the field. Styled wrapper for Field.Description from Base UI with muted foreground color.
FieldError
Error message for the field. Styled wrapper for Field.Error from Base UI with destructive foreground color.
FieldControl
Alias for Field.Control from Base UI.
FieldValidity
Alias for Field.Validity from Base UI.
Examples
Required Field
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
export default function Particle() {
return (
<Field>
<FieldLabel>
Password <span className="text-destructive-foreground">*</span>
</FieldLabel>
<Input placeholder="Enter password" required type="password" />
<FieldError>Please fill out this field.</FieldError>
</Field>
);
}
Disabled Field
This field is currently disabled.
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
export default function Particle() {
return (
<Field disabled>
<FieldLabel>Email</FieldLabel>
<Input disabled placeholder="Enter your email" type="email" />
<FieldDescription>This field is currently disabled.</FieldDescription>
</Field>
);
}
With Error
Enter an invalid email address and press enter to see the error.
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
export default function FieldWithErrorDemo() {
return (
<Field>
<FieldLabel>Email</FieldLabel>
<Input placeholder="Enter your email" type="email" />
<FieldError>Please enter a valid email address.</FieldError>
</Field>
);
}
With Validity
{
"state": {
"badInput": false,
"customError": false,
"patternMismatch": false,
"rangeOverflow": false,
"rangeUnderflow": false,
"stepMismatch": false,
"tooLong": false,
"tooShort": false,
"typeMismatch": false,
"valid": null,
"valueMissing": false
},
"error": "",
"errors": [],
"value": null,
"initialValue": null,
"validity": {
"badInput": false,
"customError": false,
"patternMismatch": false,
"rangeOverflow": false,
"rangeUnderflow": false,
"stepMismatch": false,
"tooLong": false,
"tooShort": false,
"typeMismatch": false,
"valid": null,
"valueMissing": false
}
}"use client";
import { Field, FieldLabel, FieldValidity } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
export default function FieldWithValidityDemo() {
return (
<Field>
<FieldLabel>Email</FieldLabel>
<Input placeholder="Enter your email" required type="email" />
<FieldValidity>
{(validity) => (
<div className="flex w-full flex-col gap-2">
{validity.error && (
<p className="text-destructive-foreground text-xs">
{validity.error}
</p>
)}
<div className="w-full rounded-md bg-muted p-2">
<pre className="max-h-60 overflow-y-auto font-mono text-xs [scrollbar-width:none]">
{JSON.stringify(validity, null, 2)}
</pre>
</div>
</div>
)}
</FieldValidity>
</Field>
);
}
Input Group
import { ArrowRightIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
export default function Particle() {
return (
<Field>
<FieldLabel>Subscribe</FieldLabel>
<InputGroup>
<InputGroupInput placeholder="Your best email" type="email" />
<InputGroupAddon align="inline-end">
<Button aria-label="Subscribe" size="icon-xs" variant="ghost">
<ArrowRightIcon aria-hidden="true" />
</Button>
</InputGroupAddon>
</InputGroup>
<FieldError>Please enter a valid email address.</FieldError>
</Field>
);
}
Autocomplete Field
Select a item.
"use client";
import {
Autocomplete,
AutocompleteEmpty,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
AutocompletePopup,
} from "@/components/ui/autocomplete";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const items = [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
{ label: "Grape", value: "grape" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Mango", value: "mango" },
{ label: "Pineapple", value: "pineapple" },
{ label: "Kiwi", value: "kiwi" },
{ label: "Peach", value: "peach" },
{ label: "Pear", value: "pear" },
];
export default function Particle() {
return (
<Field>
<FieldLabel>Fruits</FieldLabel>
<Autocomplete items={items}>
<AutocompleteInput
aria-label="Search items"
placeholder="Search items…"
/>
<AutocompletePopup>
<AutocompleteEmpty>No items found.</AutocompleteEmpty>
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.value} value={item}>
{item.label}
</AutocompleteItem>
)}
</AutocompleteList>
</AutocompletePopup>
</Autocomplete>
<FieldDescription>Select a item.</FieldDescription>
</Field>
);
}
Combobox Field
Select a item.
"use client";
import {
Combobox,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxPopup,
} from "@/components/ui/combobox";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const items = [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
{ label: "Grape", value: "grape" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Mango", value: "mango" },
{ label: "Pineapple", value: "pineapple" },
{ label: "Kiwi", value: "kiwi" },
{ label: "Peach", value: "peach" },
{ label: "Pear", value: "pear" },
];
export default function Particle() {
return (
<Field>
<FieldLabel>Fruits</FieldLabel>
<Combobox items={items}>
<ComboboxInput
aria-label="Select an item"
placeholder="Select an item..."
/>
<ComboboxPopup>
<ComboboxEmpty>No results found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxPopup>
</Combobox>
<FieldDescription>Select a item.</FieldDescription>
</Field>
);
}
Combobox Multiple Field
Select multiple items.
"use client";
import {
Combobox,
ComboboxChip,
ComboboxChips,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxPopup,
ComboboxValue,
} from "@/components/ui/combobox";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
const items = [
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Orange", value: "orange" },
{ label: "Grape", value: "grape" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Mango", value: "mango" },
{ label: "Pineapple", value: "pineapple" },
{ label: "Kiwi", value: "kiwi" },
{ label: "Peach", value: "peach" },
{ label: "Pear", value: "pear" },
];
export default function Particle() {
return (
<Field>
<FieldLabel>Fruits</FieldLabel>
<Combobox defaultValue={[items[0], items[4]]} items={items} multiple>
<ComboboxChips>
<ComboboxValue>
{(value: { value: string; label: string }[]) => (
<>
{value?.map((item) => (
<ComboboxChip aria-label={item.label} key={item.value}>
{item.label}
</ComboboxChip>
))}
<ComboboxInput
aria-label="Select items"
placeholder={value.length > 0 ? undefined : "Select items…"}
/>
</>
)}
</ComboboxValue>
</ComboboxChips>
<ComboboxPopup>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxPopup>
</Combobox>
<FieldDescription>Select multiple items.</FieldDescription>
</Field>
);
}
Textarea Field
Write a short bio. Maximum 500 characters.
"use client";
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Textarea } from "@/components/ui/textarea";
export default function Particle() {
return (
<Field>
<FieldLabel>Bio</FieldLabel>
<Textarea placeholder="Tell us about yourself…" />
<FieldDescription>
Write a short bio. Maximum 500 characters.
</FieldDescription>
</Field>
);
}
Select Field
This is an optional field
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const items = [
{ label: "Select a country", value: null },
{ label: "United States", value: "us" },
{ label: "United Kingdom", value: "uk" },
{ label: "Canada", value: "ca" },
{ label: "Australia", value: "au" },
];
export default function Particle() {
return (
<Field>
<FieldLabel>Country</FieldLabel>
<Select items={items}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectPopup>
{items.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectPopup>
</Select>
<FieldDescription>This is an optional field</FieldDescription>
</Field>
);
}
Checkbox Field
import { Checkbox } from "@/components/ui/checkbox";
import { Field, FieldLabel } from "@/components/ui/field";
export default function Particle() {
return (
<Field>
<FieldLabel>
<Checkbox />
Accept terms and conditions
</FieldLabel>
</Field>
);
}
Checkbox Group Field
"use client";
import { Checkbox } from "@/components/ui/checkbox";
import { CheckboxGroup } from "@/components/ui/checkbox-group";
import { Field, FieldItem, FieldLabel } from "@/components/ui/field";
import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset";
export default function Particle() {
return (
<Field
className="gap-4"
name="frameworks"
render={(props) => <Fieldset {...props} />}
>
<FieldsetLegend className="font-medium text-sm">
Frameworks
</FieldsetLegend>
<CheckboxGroup defaultValue={["react"]}>
<FieldItem>
<FieldLabel>
<Checkbox value="react" /> React
</FieldLabel>
</FieldItem>
<FieldItem>
<FieldLabel>
<Checkbox value="vue" /> Vue
</FieldLabel>
</FieldItem>
<FieldItem>
<FieldLabel>
<Checkbox value="svelte" /> Svelte
</FieldLabel>
</FieldItem>
</CheckboxGroup>
</Field>
);
}
Radio Group Field
"use client";
import {
Field,
FieldDescription,
FieldItem,
FieldLabel,
} from "@/components/ui/field";
import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset";
import { Radio, RadioGroup } from "@/components/ui/radio-group";
export default function Particle() {
return (
<Field
className="gap-4"
name="plan"
render={(props) => <Fieldset {...props} />}
>
<FieldsetLegend className="font-medium text-sm">
Choose Plan
</FieldsetLegend>
<RadioGroup defaultValue="free">
<FieldItem>
<FieldLabel>
<Radio value="free" /> Free
</FieldLabel>
</FieldItem>
<FieldItem>
<FieldLabel>
<Radio value="pro" /> Pro
</FieldLabel>
</FieldItem>
<FieldItem>
<FieldLabel>
<Radio value="enterprise" /> Enterprise
</FieldLabel>
</FieldItem>
</RadioGroup>
<FieldDescription>Select the plan that fits your needs.</FieldDescription>
</Field>
);
}
Switch Field
import { Field, FieldLabel } from "@/components/ui/field";
import { Switch } from "@/components/ui/switch";
export default function Particle() {
return (
<Field>
<FieldLabel>
<Switch />
Email notifications
</FieldLabel>
</Field>
);
}
Slider Field
This is an optional field
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Slider } from "@/components/ui/slider";
export default function Particle() {
return (
<Field className="items-stretch gap-3">
<FieldLabel>Country</FieldLabel>
<Slider defaultValue={50} />
<FieldDescription>This is an optional field</FieldDescription>
</Field>
);
}
Number Field
Choose a value between 1 and 100.
import { Field, FieldDescription } from "@/components/ui/field";
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
NumberFieldScrubArea,
} from "@/components/ui/number-field";
export default function Particle() {
return (
<Field>
<NumberField defaultValue={1} max={100} min={1}>
<NumberFieldScrubArea label="Quantity" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
<FieldDescription>Choose a value between 1 and 100.</FieldDescription>
</Field>
);
}
Complete Form Example
"use client";
import type { FormEvent } from "react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
Field,
FieldDescription,
FieldError,
FieldLabel,
} from "@/components/ui/field";
import { Form } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
export default function Particle() {
const [loading, setLoading] = useState(false);
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
setLoading(true);
await new Promise((r) => setTimeout(r, 800));
setLoading(false);
const data = {
email: formData.get("email"),
fullName: formData.get("fullName"),
newsletter: formData.get("newsletter"),
role: formData.get("role"),
};
alert(
`Full name: ${data.fullName || ""}\nEmail: ${data.email || ""}\nRole: ${
data.role || ""
}\nNewsletter: ${data.newsletter}`,
);
};
return (
<Form onSubmit={onSubmit}>
<Field name="fullName">
<FieldLabel>
Full Name <span className="text-destructive">*</span>
</FieldLabel>
<Input disabled={loading} placeholder="John Doe" required type="text" />
<FieldError>Please enter a valid name.</FieldError>
</Field>
<Field name="email">
<FieldLabel>
Email <span className="text-destructive">*</span>
</FieldLabel>
<Input
disabled={loading}
placeholder="[email protected]"
required
type="email"
/>
<FieldError>Please enter a valid email.</FieldError>
</Field>
<Field name="role">
<FieldLabel>Role</FieldLabel>
<Select
disabled={loading}
items={[
{ label: "Select your role", value: null },
{ label: "Developer", value: "developer" },
{ label: "Designer", value: "designer" },
{ label: "Product Manager", value: "manager" },
{ label: "Other", value: "other" },
]}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectPopup>
<SelectItem value="developer">Developer</SelectItem>
<SelectItem value="designer">Designer</SelectItem>
<SelectItem value="manager">Product Manager</SelectItem>
<SelectItem value="other">Other</SelectItem>
</SelectPopup>
</Select>
<FieldDescription>This is an optional field</FieldDescription>
</Field>
<Field name="newsletter">
<div className="flex items-center gap-2">
<Checkbox disabled={loading} />
<FieldLabel className="cursor-pointer">
Subscribe to newsletter
</FieldLabel>
</div>
</Field>
<Button disabled={loading} type="submit">
Submit
</Button>
</Form>
);
}
On This Page