Components
- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Dialog
- EmptyNew
- Field
- Fieldset
- Form
- Frame
- Group
- Input
- Input GroupNew
- KbdNew
- Label
- Menu
- Meter
- Number Field
- Pagination
- Popover
- Preview Card
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- SkeletonNew
- Slider
- SpinnerNew
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Toolbar
- Tooltip
Resources
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>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="no-scrollbar max-h-60 overflow-y-auto font-mono text-xs">
{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 />
</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
Apple
Strawberry
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,
FieldDescription,
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"]}>
<FieldLabel>
<Checkbox value="react" /> React
</FieldLabel>
<FieldLabel>
<Checkbox value="vue" /> Vue
</FieldLabel>
<FieldLabel>
<Checkbox value="svelte" /> Svelte
</FieldLabel>
</CheckboxGroup>
<FieldDescription>Select one or more frameworks.</FieldDescription>
</Field>
);
}
Radio Group Field
"use client";
import {
Field,
FieldDescription,
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">
<FieldLabel>
<Radio value="free" /> Free
</FieldLabel>
<FieldLabel>
<Radio value="pro" /> Pro
</FieldLabel>
<FieldLabel>
<Radio value="enterprise" /> Enterprise
</FieldLabel>
</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 * as React 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] = React.useState(false);
const onSubmit = async (e: React.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>
<FieldLabel>
Full Name <span className="text-destructive">*</span>
</FieldLabel>
<Input
disabled={loading}
name="fullName"
placeholder="John Doe"
required
type="text"
/>
<FieldError>Please enter a valid name.</FieldError>
</Field>
<Field>
<FieldLabel>
Email <span className="text-destructive">*</span>
</FieldLabel>
<Input
disabled={loading}
name="email"
placeholder="[email protected]"
required
type="email"
/>
<FieldError>Please enter a valid email.</FieldError>
</Field>
<Field>
<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" },
]}
name="role"
>
<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>
<div className="flex items-center gap-2">
<Checkbox disabled={loading} name="newsletter" />
<FieldLabel className="cursor-pointer">
Subscribe to newsletter
</FieldLabel>
</div>
</Field>
<Button disabled={loading} type="submit">
Submit
</Button>
</Form>
);
}