Components
API reference for all 50 CUCL components β props, variants, and usage examples
Components
All components are exported from @cookest/ui. Each one is fully typed, dark-mode-aware, and accessible by default.
import {
Accordion, Alert, AlertDialog, AspectRatio, Avatar, Badge, Breadcrumb, Button,
Calendar, Card, Carousel, ChartContainer, Checkbox, Collapsible, Command,
ContextMenu, Dialog, Divider, Drawer, DropdownMenu, Form, HoverCard, Input,
InputOTP, Label, Menubar, Modal, NavigationMenu, Pagination, Popover, Progress,
RadioGroup, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea,
Separator, Sheet, Sidebar, Skeleton, Slider, SonnerToaster, Spinner, Switch,
Table, Tabs, Textarea, Toast, Toaster, Toggle, ToggleGroup, Tooltip,
} from "@cookest/ui";
import "@cookest/ui/styles.css";Compound APIs expose additional helpers such as DialogContent, SheetTrigger, FormField, TableHead, and useToast. Each section below calls out the extra exports that belong to that component family.
Button
Animated button with four variants and a built-in loading spinner.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "ghost" | "danger" | "primary" | Visual style |
size | "sm" | "md" | "lg" | "md" | Button size |
loading | boolean | false | Shows spinner and disables the button |
iconLeft | ReactNode | β | Icon rendered before the label |
iconRight | ReactNode | β | Icon rendered after the label |
fullWidth | boolean | false | Stretches to fill its container |
disabled | boolean | false | Native disabled attribute |
className | string | β | Additional Tailwind classes |
All standard <button> HTML attributes are forwarded.
Examples
// Variants
<Button variant="primary">Save changes</Button>
<Button variant="secondary">Cancel</Button>
<Button variant="ghost">Learn more</Button>
<Button variant="danger">Delete account</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
// Loading state
<Button loading>Savingβ¦</Button>
// Icon slots
<Button iconLeft={<PlusIcon />}>Add item</Button>
<Button iconRight={<ArrowRightIcon />}>Continue</Button>The button uses motion.button from Framer Motion β it lifts 1px on hover and scales to 0.98 on press. Animations are suppressed when disabled or loading is true.
Input
Text input with label, helper text, error state, and icon slots.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | β | Label text rendered above the input |
helperText | string | β | Helper text rendered below |
error | string | β | Error message β replaces helperText and styles the border red |
iconLeft | ReactNode | β | Icon in the left slot |
iconRight | ReactNode | β | Icon in the right slot |
inputSize | "sm" | "md" | "lg" | "md" | Input size (uses inputSize to avoid clash with HTML size) |
fullWidth | boolean | false | Stretches to container width |
className | string | β | Applied to the outer wrapper |
All standard <input> HTML attributes are forwarded. id is auto-generated if not provided.
Examples
// Basic
<Input label="Email" type="email" placeholder="you@example.com" />
// With helper text
<Input label="Username" helperText="3β20 characters, lowercase only" />
// Error state
<Input label="Password" type="password" error="Minimum 8 characters required" />
// Icon slots
<Input label="Search" iconLeft={<SearchIcon />} placeholder="Search recipesβ¦" />
// Full-width
<Input label="Name" fullWidth />Card
Container component with optional header, body, and footer sections.
Card props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "interactive" | "outlined" | "default" | Visual style β interactive adds hover animation |
padding | "none" | "sm" | "md" | "lg" | "md" | Uniform inner padding |
className | string | β | Additional classes |
Sub-components
CardHeader, CardBody, and CardFooter are used for structured layouts. They manage their own border and padding β do not add padding to the parent Card when using sub-components (padding="none").
Examples
// Simple card with padding
<Card>
<p>Card content</p>
</Card>
// Structured card
<Card padding="none">
<CardHeader>Recipe Details</CardHeader>
<CardBody>
<p>Ingredients and stepsβ¦</p>
</CardBody>
<CardFooter>
<Button>Start cooking</Button>
</CardFooter>
</Card>
// Interactive card (hover lift)
<Card variant="interactive" onClick={handleClick}>
<p>Click me</p>
</Card>Badge
Compact status indicator with optional dot and remove button.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "success" | "warning" | "error" | "info" | "default" | Colour variant |
size | "sm" | "md" | "lg" | "md" | Badge size |
dot | boolean | false | Shows a colour dot before the label |
removable | boolean | false | Shows a remove (Γ) button |
onRemove | () => void | β | Called when the remove button is clicked |
className | string | β | Additional classes |
Examples
<Badge variant="success">Active</Badge>
<Badge variant="warning" dot>Expiring soon</Badge>
<Badge variant="error" size="sm">Out of stock</Badge>
<Badge removable onRemove={() => removeTag(id)}>Vegan</Badge>Avatar
User avatar with image, initials fallback, and group stacking.
Avatar props
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | β | Image URL β renders <img> when provided |
alt | string | required | Alt text (also used to derive initials) |
initials | string | β | Override auto-derived initials |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Avatar size |
className | string | β | Additional classes |
When src is absent the component renders a <span> with the first two initials on a sage green background.
AvatarGroup props
| Prop | Type | Default | Description |
|---|---|---|---|
max | number | β | Maximum avatars to show β overflow rendered as +N |
children | ReactNode | required | Avatar components |
className | string | β | Additional classes |
Examples
// With image
<Avatar src="/users/alice.jpg" alt="Alice Smith" size="lg" />
// Initials fallback
<Avatar alt="Bob Jones" size="md" />
// Group
<AvatarGroup max={3}>
<Avatar alt="Alice Smith" />
<Avatar alt="Bob Jones" />
<Avatar alt="Carol White" />
<Avatar alt="Dave Green" />
</AvatarGroup>Modal
Accessible dialog overlay with focus trap and keyboard dismiss.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | required | Controls visibility |
onClose | () => void | required | Called when dismissed |
title | string | β | Renders a header row with close button |
size | "sm" | "md" | "lg" | "md" | Dialog max-width |
closeOnBackdrop | boolean | true | Dismiss when clicking the backdrop |
closeOnEsc | boolean | true | Dismiss on Escape key |
footer | ReactNode | β | Content in the footer row |
className | string | β | Applied to the dialog panel |
Example
const [open, setOpen] = useState(false);
<Button onClick={() => setOpen(true)}>Open modal</Button>
<Modal
open={open}
onClose={() => setOpen(false)}
title="Confirm deletion"
footer={
<>
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="danger" onClick={handleDelete}>Delete</Button>
</>
}
>
<p>This action cannot be undone.</p>
</Modal>The Modal manages document.body.style.overflow to prevent scroll while open and restores focus to the previously active element on close.
Tooltip
Positioned tooltip with an animated arrow.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
content | ReactNode | required | Tooltip text or JSX |
position | "top" | "bottom" | "left" | "right" | "top" | Arrow direction |
delay | number | 0 | Show delay in milliseconds |
className | string | β | Applied to the tooltip panel |
children | ReactNode | required | Trigger element |
Example
<Tooltip content="Save your changes" position="top">
<Button variant="ghost" size="sm">
<SaveIcon />
</Button>
</Tooltip>Toggle
Switch input with an optional label.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | required | Toggle state |
onChange | (checked: boolean) => void | required | State change handler |
label | string | β | Label text |
size | "sm" | "md" | "lg" | "md" | Toggle size |
disabled | boolean | false | Prevents interaction |
className | string | β | Additional classes |
Example
const [enabled, setEnabled] = useState(false);
<Toggle
checked={enabled}
onChange={setEnabled}
label="Enable notifications"
/>Select
Custom dropdown with keyboard navigation and optional search.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | SelectOption[] | required | Array of { value, label, disabled? } |
value | string | β | Controlled selected value |
onChange | (value: string) => void | β | Selection handler |
placeholder | string | "Selectβ¦" | Placeholder text |
label | string | β | Label above the trigger |
error | string | β | Error message below |
disabled | boolean | false | Prevents interaction |
searchable | boolean | false | Adds a search input inside the dropdown |
className | string | β | Applied to the wrapper |
Example
const [diet, setDiet] = useState("");
<Select
label="Dietary preference"
options={[
{ value: "none", label: "No preference" },
{ value: "vegan", label: "Vegan" },
{ value: "vegetarian", label: "Vegetarian" },
{ value: "gluten-free", label: "Gluten-free" },
]}
value={diet}
onChange={setDiet}
searchable
/>Skeleton
Loading placeholder with a pulse animation.
Skeleton props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "text" | "circular" | "rectangular" | "rectangular" | Shape |
width | string | number | β | CSS width value or px number |
height | string | number | β | CSS height value or px number |
lines | number | 1 | Number of text lines (variant="text" only) |
className | string | β | Additional classes |
SkeletonCard
Pre-built card skeleton for loading states:
<SkeletonCard />Examples
// Text lines
<Skeleton variant="text" lines={3} />
// Avatar placeholder
<Skeleton variant="circular" width={40} height={40} />
// Image placeholder
<Skeleton variant="rectangular" width="100%" height={200} />
// Full card
<SkeletonCard />Alert
Contextual feedback banner with animated entrance and dismiss.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "info" | "success" | "warning" | "error" | "info" | Colour and icon variant |
title | string | β | Bold title row |
dismissible | boolean | false | Shows a dismiss (Γ) button |
onDismiss | () => void | β | Called when dismissed |
icon | ReactNode | β | Override the default variant icon |
visible | boolean | true | Controls AnimatePresence visibility |
className | string | β | Additional classes |
Examples
// Static info banner
<Alert variant="info" title="Did you know?">
You can save up to 20% by switching to the Pro plan.
</Alert>
// Dismissible error
const [show, setShow] = useState(true);
<Alert
variant="error"
title="Upload failed"
dismissible
visible={show}
onDismiss={() => setShow(false)}
>
The file exceeds the 10 MB limit.
</Alert>Divider
Visual separator, horizontal or vertical.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" | Axis |
label | string | β | Centered label text (horizontal only) |
className | string | β | Additional classes |
Examples
// Horizontal
<Divider />
// With label
<Divider label="or continue with" />
// Vertical (needs a fixed height context)
<div className="flex h-8 items-center gap-4">
<span>Option A</span>
<Divider orientation="vertical" />
<span>Option B</span>
</div>Accordion
Data-driven accordion for FAQs, settings groups, and expandable content blocks.
Import
import { Accordion } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | AccordionItem[] | required | Array of sections to render |
defaultOpen | string | string[] | β | Initially open item ID(s) |
multiple | boolean | false | Allow more than one open item |
variant | "default" | "bordered" | "separated" | "default" | Visual style |
size | "sm" | "md" | "lg" | "md" | Header/content sizing |
className | string | β | Wrapper classes |
AccordionItem has the shape { id, title, subtitle?, icon?, content, disabled? }.
Example
<Accordion
items={[
{
id: "pantry",
title: "Pantry sync",
subtitle: "2 items expiring soon",
content: <p>Your pantry data syncs automatically.</p>,
},
]}
defaultOpen="pantry"
/>AlertDialog
Confirmation dialog for destructive or high-stakes actions.
Import
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
AlertDialog | React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Root> | Root open-state container |
AlertDialogContent | React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> | Main dialog surface |
AlertDialogTitle | React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title> | Title text |
AlertDialogDescription | React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description> | Supporting copy |
AlertDialogAction | React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> | Primary action button |
AlertDialogCancel | React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel> | Secondary cancel button |
Example
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="danger">Delete recipe</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete this recipe?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>AspectRatio
Keeps media, embeds, and cards locked to a specific ratio.
Import
import { AspectRatio } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
AspectRatio | React.ComponentPropsWithoutRef<typeof AspectRatioPrimitive.Root> | Use the Radix ratio prop to control width/height proportion |
Example
<AspectRatio ratio={16 / 9}>
<img src="/recipe.jpg" alt="Recipe" className="h-full w-full rounded-lg object-cover" />
</AspectRatio>Breadcrumb
Hierarchical navigation trail for dashboards and deep flows.
Import
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Breadcrumb | React.ComponentPropsWithoutRef<"nav"> & { separator?: React.ReactNode } | Wraps the breadcrumb nav landmark |
BreadcrumbLink | React.ComponentPropsWithoutRef<"a"> & { asChild?: boolean } | Supports Radix Slot composition |
BreadcrumbSeparator | React.ComponentProps<"li"> | Defaults to a chevron separator |
BreadcrumbEllipsis | React.ComponentProps<"span"> | Compact overflow marker |
Example
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/docs">Docs</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Components</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>Calendar
Themed react-day-picker wrapper for single, range, or multi-date selection.
Import
import { Calendar } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
showOutsideDays | boolean | true | Show leading/trailing days from adjacent months |
className | string | β | Outer wrapper classes |
classNames | CalendarProps["classNames"] | β | Override internal DayPicker class mappings |
...props | React.ComponentProps<typeof DayPicker> | β | All native react-day-picker props are forwarded |
Example
const [selected, setSelected] = useState<Date | undefined>(new Date());
<Calendar mode="single" selected={selected} onSelect={setSelected} />Carousel
Embla-powered carousel with built-in navigation buttons and orientation support.
Import
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
opts | CarouselOptions | β | Embla carousel configuration |
plugins | CarouselPlugin | β | Embla plugins array |
orientation | "horizontal" | "vertical" | "horizontal" | Slide axis |
setApi | (api: CarouselApi) => void | β | Receive the Embla API instance |
...props | React.HTMLAttributes<HTMLDivElement> | β | Standard wrapper props |
CarouselPrevious and CarouselNext also accept Button props (minus required children).
Example
<Carousel className="w-full max-w-md">
<CarouselContent>
{[1, 2, 3].map((item) => (
<CarouselItem key={item}>
<Card>
<p>Slide {item}</p>
</Card>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Chart
Recharts helpers for theme-aware chart containers, tooltips, and legends.
Import
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
ChartContainer | React.ComponentProps<"div"> & { config: ChartConfig; children: ResponsiveContainer["children"] } | Provides chart theme/config context |
ChartTooltip | typeof RechartsPrimitive.Tooltip | Re-exported Recharts tooltip primitive |
ChartTooltipContent | React.ComponentProps<typeof RechartsPrimitive.Tooltip> & React.ComponentProps<"div"> & { hideLabel?: boolean; hideIndicator?: boolean; indicator?: "line" | "dot" | "dashed"; nameKey?: string; labelKey?: string } | Styled tooltip renderer |
ChartLegend | typeof RechartsPrimitive.Legend | Re-exported Recharts legend primitive |
ChartLegendContent | React.ComponentProps<"div"> & Pick<LegendProps, "payload" | "verticalAlign"> & { hideIcon?: boolean; nameKey?: string } | Styled legend renderer |
ChartConfig maps series keys to labels, icons, and colors/theme overrides.
Example
import { Bar, BarChart, XAxis } from "recharts";
const chartConfig = {
protein: { label: "Protein", color: "#7A9A65" },
};
<ChartContainer config={chartConfig} className="h-64">
<BarChart data={[{ day: "Mon", protein: 24 }]}>
<XAxis dataKey="day" />
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
<Bar dataKey="protein" fill="var(--color-protein)" />
</BarChart>
</ChartContainer>Checkbox
Accessible checkbox built on Radix with checked and indeterminate support.
Import
import { Checkbox } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Checkbox | React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> | Supports checked, defaultChecked, onCheckedChange, disabled, and other Radix checkbox props |
Example
<Checkbox checked={checked} onCheckedChange={setChecked} aria-label="Select ingredient" />Collapsible
Lightweight expandable container for inline disclosure UI.
Import
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Collapsible | React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.Root> | Controls open state |
CollapsibleTrigger | React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleTrigger> | Toggle control |
CollapsibleContent | React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent> | Expandable body |
Example
<Collapsible>
<CollapsibleTrigger asChild>
<Button variant="ghost">Show nutrition notes</Button>
</CollapsibleTrigger>
<CollapsibleContent>
<p>Extra nutritional guidance goes here.</p>
</CollapsibleContent>
</Collapsible>Command
Command palette and searchable action list powered by cmdk.
Import
import {
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandShortcut,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Command | React.ComponentPropsWithoutRef<typeof CommandPrimitive> | Root command surface |
CommandDialog | React.ComponentPropsWithoutRef<typeof Dialog> | Wraps the command list inside the CUCL Dialog |
CommandInput | React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> | Search field |
CommandItem | React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> | Selectable result row |
CommandShortcut | React.HTMLAttributes<HTMLSpanElement> | Right-aligned shortcut text |
Example
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Search actions..." />
<CommandList>
<CommandEmpty>No results.</CommandEmpty>
<CommandGroup heading="Recipes">
<CommandItem>
Generate recipe
<CommandShortcut>βK</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>ContextMenu
Right-click or long-press menu for contextual actions.
Import
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
ContextMenu | React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Root> | Root state container |
ContextMenuContent | React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> | Floating menu surface |
ContextMenuItem | React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { inset?: boolean } | Optional inset padding |
ContextMenuSubTrigger | React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean } | Opens a submenu |
ContextMenuLabel | React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & { inset?: boolean } | Section label |
ContextMenuShortcut | React.HTMLAttributes<HTMLSpanElement> | Keyboard shortcut label |
Example
<ContextMenu>
<ContextMenuTrigger asChild>
<Card>
<p>Right-click this card</p>
</Card>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>Rename</ContextMenuItem>
<ContextMenuItem>Duplicate</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>Dialog
Radix-based modal dialog for fully custom overlays.
Import
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Dialog | React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> | Root open-state container |
DialogContent | React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> | Main dialog surface |
DialogHeader | React.HTMLAttributes<HTMLDivElement> | Header layout helper |
DialogFooter | React.HTMLAttributes<HTMLDivElement> | Footer action row |
DialogTitle | React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> | Title text |
DialogDescription | React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> | Description text |
Example
<Dialog>
<DialogTrigger asChild>
<Button>Open dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Share recipe</DialogTitle>
<DialogDescription>Copy a link or invite collaborators.</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button>Done</Button>
</DialogFooter>
</DialogContent>
</Dialog>Drawer
Bottom-sheet style panel powered by vaul.
Import
import {
Drawer,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Drawer | React.ComponentProps<typeof DrawerPrimitive.Root> | shouldScaleBackground defaults to true |
DrawerContent | React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> | Bottom sheet surface |
DrawerHeader | React.HTMLAttributes<HTMLDivElement> | Header layout helper |
DrawerFooter | React.HTMLAttributes<HTMLDivElement> | Footer action stack |
DrawerTitle | React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title> | Title text |
DrawerDescription | React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description> | Supporting copy |
Example
<Drawer>
<DrawerTrigger asChild>
<Button>Open drawer</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Quick actions</DrawerTitle>
<DrawerDescription>Choose the next step.</DrawerDescription>
</DrawerHeader>
<DrawerFooter>
<Button>Continue</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>DropdownMenu
Button-triggered action menu with checkbox, radio, and submenu support.
Import
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
DropdownMenu | React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root> | Root menu state |
DropdownMenuContent | React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> | sideOffset defaults to 4 |
DropdownMenuItem | React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean } | Optional inset padding |
DropdownMenuSubTrigger | React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean } | Opens nested menus |
DropdownMenuLabel | React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean } | Section label |
DropdownMenuShortcut | React.HTMLAttributes<HTMLSpanElement> | Shortcut text slot |
Example
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost">Open menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Form
Helpers for pairing react-hook-form with CUCL inputs and accessible field messaging.
Import
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Form | typeof FormProvider | Wraps the React Hook Form context |
FormField | ControllerProps<TFieldValues, TName> | Connects a single controlled field |
FormItem | React.HTMLAttributes<HTMLDivElement> | Provides IDs for label/description/message wiring |
FormLabel | React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> | Label tied to the current FormField |
FormControl | React.ComponentPropsWithoutRef<typeof Slot> | Injects ARIA attributes into the control |
FormDescription | React.HTMLAttributes<HTMLParagraphElement> | Help text |
FormMessage | React.HTMLAttributes<HTMLParagraphElement> | Validation error text |
Example
const form = useForm({ defaultValues: { email: "" } });
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>HoverCard
Preview card that appears on hover or focus.
Import
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
HoverCard | React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Root> | Root state container |
HoverCardTrigger | React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Trigger> | Trigger element |
HoverCardContent | React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> | align="center" and sideOffset={4} by default |
Example
<HoverCard>
<HoverCardTrigger asChild>
<Button variant="ghost">Hover for preview</Button>
</HoverCardTrigger>
<HoverCardContent>
<p>Quick recipe summary.</p>
</HoverCardContent>
</HoverCard>InputOTP
Segmented one-time-password input built on input-otp.
Import
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
InputOTP | React.ComponentPropsWithoutRef<typeof OTPInput> | Also supports containerClassName styling |
InputOTPGroup | React.ComponentPropsWithoutRef<"div"> | Groups slots visually |
InputOTPSlot | React.ComponentPropsWithoutRef<"div"> & { index: number } | Slot renderer for a specific position |
InputOTPSeparator | React.ComponentPropsWithoutRef<"div"> | Visual separator between groups |
Example
<InputOTP maxLength={6} value={code} onChange={setCode}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>Label
Form label primitive with disabled-peer styling.
Import
import { Label } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Label | React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> | Forwarded Radix label props |
Example
<Label htmlFor="servings">Servings</Label>Menubar
Desktop-style menubar with submenus, checkbox items, and radio groups.
Import
import {
Menubar,
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Menubar | React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root> | Top-level menubar container |
MenubarContent | React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content> | Defaults: align="start", alignOffset={-4}, sideOffset={8} |
MenubarItem | React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & { inset?: boolean } | Menu row with optional inset |
MenubarSubTrigger | React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & { inset?: boolean } | Opens a submenu |
MenubarLabel | React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & { inset?: boolean } | Group label |
MenubarShortcut | React.HTMLAttributes<HTMLSpanElement> | Shortcut text slot |
Example
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>New recipe</MenubarItem>
<MenubarItem>Export</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>NavigationMenu
Navigation bar primitive for multi-level site or dashboard menus.
Import
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
NavigationMenu | React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> | Root wrapper that also renders the viewport |
NavigationMenuList | React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List> | Horizontal item list |
NavigationMenuTrigger | React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> | Trigger button for content |
NavigationMenuContent | React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content> | Dropdown content panel |
navigationMenuTriggerStyle | string | Shared trigger class string for custom link implementations |
Example
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Docs</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="/docs/backend">Backend</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>Pagination
Composable pagination navigation with previous/next links and ellipsis helpers.
Import
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Pagination | React.ComponentProps<"nav"> | Root nav landmark |
PaginationContent | React.ComponentProps<"ul"> | Inner list wrapper |
PaginationItem | React.ComponentProps<"li"> | Single list item |
PaginationLink | React.ComponentProps<"a"> & Pick<ButtonProps, "size"> & { isActive?: boolean } | Page link styled like a button |
PaginationPrevious | React.ComponentProps<typeof PaginationLink> | Previous shortcut |
PaginationNext | React.ComponentProps<typeof PaginationLink> | Next shortcut |
Example
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="?page=1" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="?page=2" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="?page=3" />
</PaginationItem>
</PaginationContent>
</Pagination>Popover
Floating panel anchored to a trigger or custom anchor.
Import
import { Popover, PopoverContent, PopoverTrigger } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Popover | React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root> | Root state container |
PopoverTrigger | React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger> | Trigger element |
PopoverAnchor | React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Anchor> | Optional custom anchor |
PopoverContent | React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> | Defaults: align="center", sideOffset={4} |
Example
<Popover>
<PopoverTrigger asChild>
<Button variant="ghost">Open popover</Button>
</PopoverTrigger>
<PopoverContent>
<p>Popover content</p>
</PopoverContent>
</Popover>Progress
Linear progress indicator with labels, variants, and optional animation.
Import
import { Progress } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | β | Current progress value |
label | string | β | Optional label above the bar |
showValue | boolean | false | Display the numeric value |
size | "xs" | "sm" | "md" | "lg" | "md" | Track height |
color | "primary" | "success" | "warning" | "error" | "primary" | Bar color |
striped | boolean | false | Apply striped fill styling |
animated | boolean | false | Animate stripes |
rounded | boolean | true | Round the bar corners |
className | string | β | Wrapper classes |
Example
<Progress value={72} label="Meal plan completion" showValue color="success" />RadioGroup
Single-selection option group built on Radix radio primitives.
Import
import { RadioGroup, RadioGroupItem } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
RadioGroup | React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> | Supports controlled and uncontrolled selection |
RadioGroupItem | React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> | Individual option |
Example
<RadioGroup value={planType} onValueChange={setPlanType}>
<div className="flex items-center gap-2">
<RadioGroupItem value="balanced" id="balanced" />
<Label htmlFor="balanced">Balanced</Label>
</div>
</RadioGroup>Resizable
Composable panel layout powered by react-resizable-panels.
Import
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
ResizablePanelGroup | React.ComponentProps<typeof Group> | orientation defaults to "horizontal" |
ResizablePanel | typeof Panel | Individual resizable panel |
ResizableHandle | React.ComponentProps<typeof Separator> & { withHandle?: boolean } | Divider between panels; optional visible grab handle |
Example
<ResizablePanelGroup className="min-h-[320px] rounded-lg border">
<ResizablePanel defaultSize={35}>Filters</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={65}>Results</ResizablePanel>
</ResizablePanelGroup>ScrollArea
Custom scroll container with themed scrollbars.
Import
import { ScrollArea, ScrollBar } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
ScrollArea | React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> | Root scroll viewport |
ScrollBar | React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> | Custom scrollbar |
Example
<ScrollArea className="h-48 rounded-md border p-4">
<div className="space-y-2">...</div>
<ScrollBar orientation="vertical" />
</ScrollArea>Separator
Minimal separator primitive for layout composition.
Import
import { Separator } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" | Axis |
decorative | boolean | true | Marks the separator as presentational |
className | string | β | Additional classes |
...props | React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> | β | Forwarded Radix separator props |
Example
<div className="flex h-8 items-center gap-4">
<span>Left</span>
<Separator orientation="vertical" />
<span>Right</span>
</div>Sheet
Side panel built on the Radix dialog primitives.
Import
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Sheet | React.ComponentPropsWithoutRef<typeof SheetPrimitive.Root> | Root open-state container |
SheetContent | React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> & { side?: "top" | "right" | "bottom" | "left" } | side defaults to "right" |
SheetHeader | React.HTMLAttributes<HTMLDivElement> | Header layout helper |
SheetFooter | React.HTMLAttributes<HTMLDivElement> | Footer action row |
SheetTitle | React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> | Title text |
SheetDescription | React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> | Supporting copy |
Example
<Sheet>
<SheetTrigger asChild>
<Button>Open sheet</Button>
</SheetTrigger>
<SheetContent side="right">
<SheetHeader>
<SheetTitle>Recipe filters</SheetTitle>
<SheetDescription>Adjust cuisine and time limits.</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>Sidebar
Responsive application sidebar with mobile-sheet fallback and rich menu helpers.
Import
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupLabel,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
SidebarProvider | React.ComponentProps<"div"> & { defaultOpen?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void } | Also wires the β/Ctrl+b toggle shortcut |
Sidebar | React.ComponentProps<"div"> & { side?: "left" | "right"; variant?: "sidebar" | "floating" | "inset"; collapsible?: "offcanvas" | "icon" | "none" } | Main sidebar surface |
SidebarMenuButton | React.ComponentProps<"button"> & { asChild?: boolean; isActive?: boolean; tooltip?: string | React.ComponentProps<typeof TooltipContent> } | Primary menu action |
SidebarMenuAction | React.ComponentProps<"button"> & { asChild?: boolean; showOnHover?: boolean } | Secondary per-item action |
SidebarGroupLabel | React.ComponentProps<"div"> & { asChild?: boolean } | Group heading |
SidebarMenuSubButton | React.ComponentProps<"a"> & { asChild?: boolean; isActive?: boolean } | Nested link button |
Example
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Cookest</SidebarGroupLabel>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive>Recipes</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<SidebarInset>
<SidebarTrigger />
</SidebarInset>
</SidebarProvider>Slider
Single-value slider with labels, marks, sizes, and color variants.
Import
import { Slider } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | β | Controlled value |
defaultValue | number | β | Uncontrolled starting value |
min | number | 0 | Minimum value |
max | number | 100 | Maximum value |
step | number | 1 | Step size |
label | string | β | Label above the control |
showValue | boolean | false | Show current numeric value |
disabled | boolean | false | Disable interaction |
size | "sm" | "md" | "lg" | "md" | Track/thumb size |
color | "primary" | "success" | "warning" | "error" | "primary" | Active track color |
marks | SliderMark[] | β | Tick labels { value, label? } |
onChange | (value: number) => void | β | Value change callback |
className | string | β | Wrapper classes |
Example
<Slider
min={0}
max={60}
value={minutes}
onChange={setMinutes}
showValue
marks={[{ value: 15 }, { value: 30 }, { value: 45 }]}
/>Sonner
Thin wrapper around sonner for app-wide toast notifications.
Import
import { SonnerToaster } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
SonnerToaster | React.ComponentProps<typeof Sonner> | Re-exported as SonnerToaster from @cookest/ui |
Example
<>
<SonnerToaster richColors closeButton />
</>Spinner
Loading spinner with size, color, and accessible label support.
Import
import { Spinner } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Spinner size |
color | "primary" | "white" | "current" | "primary" | Stroke color |
label | string | β | Screen-reader label |
className | string | β | Additional classes |
Example
<Spinner size="lg" label="Loading recipes" />Switch
Binary on/off switch using Radix switch primitives.
Import
import { Switch } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Switch | React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> | Supports checked, defaultChecked, onCheckedChange, and disabled |
Example
<Switch checked={usePantry} onCheckedChange={setUsePantry} aria-label="Use pantry" />Table
Composable table primitives with styled headers, rows, and captions.
Import
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
Table | React.HTMLAttributes<HTMLTableElement> | Wrapped in an overflow container |
TableHeader | React.HTMLAttributes<HTMLTableSectionElement> | <thead> helper |
TableBody | React.HTMLAttributes<HTMLTableSectionElement> | <tbody> helper |
TableFooter | React.HTMLAttributes<HTMLTableSectionElement> | <tfoot> helper |
TableHead | React.ThHTMLAttributes<HTMLTableCellElement> | Header cell |
TableCell | React.TdHTMLAttributes<HTMLTableCellElement> | Data cell |
TableCaption | React.HTMLAttributes<HTMLTableCaptionElement> | Caption text |
Example
<Table>
<TableHeader>
<TableRow>
<TableHead>Ingredient</TableHead>
<TableHead>Qty</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Eggs</TableCell>
<TableCell>4</TableCell>
</TableRow>
</TableBody>
</Table>Tabs
Data-driven tabs component with underline, pill, and boxed variants.
Import
import { Tabs } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | TabItem[] | required | Tab definitions to render |
defaultTab | string | β | Initially selected tab ID |
value | string | β | Controlled tab ID |
onChange | (id: string) => void | β | Tab change callback |
variant | "underline" | "pills" | "boxed" | "underline" | Visual style |
size | "sm" | "md" | "lg" | "md" | Trigger sizing |
fullWidth | boolean | false | Stretch triggers to fill width |
className | string | β | Wrapper classes |
TabItem has the shape { id, label, icon?, disabled?, badge?, content }.
Example
<Tabs
items={[
{ id: "ingredients", label: "Ingredients", content: <p>List</p> },
{ id: "steps", label: "Steps", content: <p>Steps</p> },
]}
/>Textarea
Multi-line text input with labels, helper/error text, character counts, and auto-resize.
Import
import { Textarea } from "@cookest/ui";Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | β | Label text above the field |
helperText | string | β | Help text below the field |
error | string | β | Error message below the field |
maxLength | number | β | Native maximum length |
showCount | boolean | false | Show current character count |
resize | "none" | "vertical" | "horizontal" | "both" | "vertical" | CSS resize behavior |
autoResize | boolean | false | Grow the textarea with content |
inputSize | "sm" | "md" | "lg" | "md" | Control sizing |
fullWidth | boolean | false | Stretch to container width |
className | string | β | Wrapper classes |
...props | TextareaHTMLAttributes<HTMLTextAreaElement> | β | Native textarea props are forwarded |
Example
<Textarea
label="Notes"
helperText="Add substitutions or serving tips"
showCount
maxLength={280}
autoResize
/>Toast
Low-level toast primitives plus an imperative toast store.
Import
import {
Toast,
ToastAction,
ToastDescription,
ToastTitle,
Toaster,
toast,
useToast,
} from "@cookest/ui";Props
| Export | Props / return type | Notes |
|---|---|---|
Toast | React.ComponentPropsWithoutRef<typeof Toast> | Low-level toast primitive |
ToastAction | React.ReactElement<typeof ToastAction> | Optional inline action button |
Toaster | none | Mount once near the app root |
toast | (options: Omit<ToasterToast, "id">) => { id, dismiss, update } | Imperative helper from use-toast.ts |
useToast | () => { toasts, toast, dismiss } | Hook exposing current toast state and helpers |
Example
<>
<Button
onClick={() =>
toast({
title: "Saved",
description: "Recipe updated successfully.",
})
}
>
Show toast
</Button>
<Toaster />
</>ToggleGroup
Grouped toggle buttons for single or multiple selection.
Import
import { ToggleGroup, ToggleGroupItem } from "@cookest/ui";Props
| Export | Props type | Notes |
|---|---|---|
ToggleGroup | React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> & { size?: "sm" | "md" | "lg" } | Group-level size defaults to "md" |
ToggleGroupItem | React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> & { size?: "sm" | "md" | "lg" } | Item can override the inherited size |
Example
<ToggleGroup type="single" value={view} onValueChange={setView}>
<ToggleGroupItem value="list">List</ToggleGroupItem>
<ToggleGroupItem value="grid">Grid</ToggleGroupItem>
</ToggleGroup>