Dropdown menu
FreeA menu of actions anchored to a trigger, with groups, shortcuts and separators.
pnpm dlx shadcn@latest add https://caliperui.site/r/dropdown-menu.json3 files selected
When to use #
Use it for
- Row, toolbar and account menus with several actions.
- Items that deserve keyboard shortcuts or a destructive style.
Skip it when
- One action is the default; use a split button.
- The choice sets a form value; use a select.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/dropdown-menu.json
Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above. It is built from DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut and DropdownMenuConfirmItem.
import { Copy, FolderOpen, Pencil, Trash2 } from "lucide-react" import { useState } from "react" import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuConfirmItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" export function ProjectMenu() { const [status, setStatus] = useState("3 files selected"); const [sidebar, setSidebar] = useState(true); return ( <div className="grid justify-items-center gap-3 pb-48"> <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">File actions</Button> </DropdownMenuTrigger> <DropdownMenuContent align="start" className="w-64"> <DropdownMenuLabel>3 files selected</DropdownMenuLabel> <DropdownMenuItem onSelect={() => setStatus("Opened 3 files in new tabs.")}><FolderOpen aria-hidden="true" />Open<DropdownMenuShortcut>↵</DropdownMenuShortcut></DropdownMenuItem> <DropdownMenuItem onSelect={() => setStatus("Renaming Q3 reconciliation.csv…")}><Pencil aria-hidden="true" />Rename<DropdownMenuShortcut>F2</DropdownMenuShortcut></DropdownMenuItem> <DropdownMenuItem onSelect={() => setStatus("Duplicated 3 files.")}><Copy aria-hidden="true" />Duplicate<DropdownMenuShortcut>⌘D</DropdownMenuShortcut></DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuCheckboxItem checked={sidebar} onCheckedChange={(value) => setSidebar(value === true)}>Show in sidebar</DropdownMenuCheckboxItem> <DropdownMenuSeparator /> <DropdownMenuConfirmItem label="Delete 3 files" icon={<Trash2 aria-hidden="true" />} question="Delete 3 files?" detail="They move to Trash for 30 days." onConfirm={() => setStatus("Moved 3 files to Trash. Restore within 30 days.")} /> </DropdownMenuContent> </DropdownMenu> <p role="status" className="text-sm text-muted-foreground">{status}</p> </div> ); }
API reference #
DropdownMenu
component
No props of its own; it accepts the native props of its root element.
DropdownMenuTrigger
component
No props of its own; it accepts the native props of its root element.
DropdownMenuContent
component
No props of its own; it accepts the native props of its root element.
DropdownMenuGroup
component
No props of its own; it accepts the native props of its root element.
DropdownMenuItem
component
No props of its own; it accepts the native props of its root element.
DropdownMenuCheckboxItem
component
No props of its own; it accepts the native props of its root element.
DropdownMenuRadioGroup
component
No props of its own; it accepts the native props of its root element.
DropdownMenuRadioItem
component
No props of its own; it accepts the native props of its root element.
DropdownMenuLabel
component
No props of its own; it accepts the native props of its root element.
DropdownMenuSeparator
component
No props of its own; it accepts the native props of its root element.
DropdownMenuShortcut
component
No props of its own; it accepts the native props of its root element.
DropdownMenuConfirmItem
component
Accessibility #
It exposes group semantics, with aria-hidden and aria-label kept in step with state.
Keyboard and focus handling comes from Radix dropdown-menu: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.