Dropdown Menu
Action menu anchored to a Button: items with icons and shortcuts, checkbox and radio items, labels, separators, destructive variant. Arrow keys, typeahead, focus follows the pointer, Esc returns focus.
npx @kznlabs/labsui add dropdown-menu"use client";import { Copy, MoreHorizontal, Pencil, Trash2 } from "lucide-react";import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger,} from "@/components/ui/dropdown-menu";export function ClipActions({ onDelete }: { onDelete: () => void }) { return ( <DropdownMenu> <DropdownMenuTrigger variant="ghost" size="icon" aria-label="Clip actions" > <MoreHorizontal /> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuItem> <Pencil /> Rename </DropdownMenuItem> <DropdownMenuItem shortcut="⌘C"> <Copy /> Copy link </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem variant="destructive" onSelect={onDelete}> <Trash2 /> Delete </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> );}<DropdownMenu>
Action menu anchored to a button. Controlled (open + onOpenChange) or uncontrolled.
children*ReactNodeTrigger and content.
defaultOpendefaultfalsebooleanOpen at first render (uncontrolled).
onOpenChange(open: boolean) => voidCalled on the trigger, Esc, outside click or a selection.
openbooleanControlled state; pair with
onOpenChange.
| Prop | Type | Default |
|---|---|---|
children*Trigger and content. | ReactNode | — |
defaultOpenOpen at first render (uncontrolled). | boolean | false |
onOpenChangeCalled on the trigger, Esc, outside click or a selection. | (open: boolean) => void | — |
openControlled state; pair with | boolean | — |
<DropdownMenuTrigger>
A Button. Keyboard: Enter, Space or Down opens on the first item, Up on the last.
childrenReactNodeLabel; its width is reserved so loading and success never resize the button.
confirmLabelReactNodeTwo-step action: first click swaps the label to this, second click runs
onClick.confirmTimeoutnumberMs before an armed confirm step resets. Also resets on blur or Esc.
loadingbooleanDisables the button and shows a spinner before the label.
loadingLabelReactNodeLabel shown while loading, with the spinner. Defaults to
children.size"default" | "sm" | "lg" | "icon"Height and padding; "icon" is square, so give it an
aria-label.statusButtonStatusIn-place feedback:
loadingspins,successswaps the label tosuccessLabel.successLabelReactNodeText shown on
status="success", with a check icon. Defaults to "Done".swapSwapDirectionWhere the old label slides out to on a swap. Default "left".
variant"default" | "secondary" | "outline" | "ghost" | "destructive"Fill and emphasis: one primary action per view,
destructivefor irreversible ones.
| Prop | Type | Default |
|---|---|---|
childrenLabel; its width is reserved so loading and success never resize the button. | ReactNode | — |
confirmLabelTwo-step action: first click swaps the label to this, second click runs | ReactNode | — |
confirmTimeoutMs before an armed confirm step resets. Also resets on blur or Esc. | number | — |
loadingDisables the button and shows a spinner before the label. | boolean | — |
loadingLabelLabel shown while loading, with the spinner. Defaults to | ReactNode | — |
sizeHeight and padding; "icon" is square, so give it an | "default" | "sm" | "lg" | "icon" | — |
statusIn-place feedback: | ButtonStatus | — |
successLabelText shown on | ReactNode | — |
swapWhere the old label slides out to on a swap. Default "left". | SwapDirection | — |
variantFill and emphasis: one primary action per view, | "default" | "secondary" | "outline" | "ghost" | "destructive" | — |
Also accepts every Button prop.
<DropdownMenuContent>
children*ReactNodeItems, labels, groups and separators.
aligndefault"start""start" | "end"Edge of the trigger the menu lines up with.
| Prop | Type | Default |
|---|---|---|
children*Items, labels, groups and separators. | ReactNode | — |
alignEdge of the trigger the menu lines up with. | "start" | "end" | "start" |
<DropdownMenuItem>
children*ReactNodeLabel, optionally with a leading icon.
disabledbooleanShown but not selectable.
onSelect() => voidCalled on click or Enter; the menu then closes.
shortcutstringRight-aligned hint, e.g. "⌘S".
variantdefault"default""default" | "destructive""destructive": red, for irreversible actions.
| Prop | Type | Default |
|---|---|---|
children*Label, optionally with a leading icon. | ReactNode | — |
disabledShown but not selectable. | boolean | — |
onSelectCalled on click or Enter; the menu then closes. | () => void | — |
shortcutRight-aligned hint, e.g. "⌘S". | string | — |
variant"destructive": red, for irreversible actions. | "default" | "destructive" | "default" |
<DropdownMenuCheckboxItem>
Toggles without closing the menu.
checked*booleanShows the check mark.
children*ReactNodeLabel, optionally with a leading icon.
onCheckedChange*(checked: boolean) => voidCalled with the new state.
disabledbooleanShown but not selectable.
shortcutstringRight-aligned hint, e.g. "⌘S".
| Prop | Type | Default |
|---|---|---|
checked*Shows the check mark. | boolean | — |
children*Label, optionally with a leading icon. | ReactNode | — |
onCheckedChange*Called with the new state. | (checked: boolean) => void | — |
disabledShown but not selectable. | boolean | — |
shortcutRight-aligned hint, e.g. "⌘S". | string | — |
<DropdownMenuRadioGroup>
children*ReactNodeDropdownMenuRadioItems.onValueChange*(value: string) => voidCalled with the picked item's value.
value*stringValue of the checked item.
| Prop | Type | Default |
|---|---|---|
children*
| ReactNode | — |
onValueChange*Called with the picked item's value. | (value: string) => void | — |
value*Value of the checked item. | string | — |
<DropdownMenuRadioItem>
One choice of a RadioGroup; picking it keeps the menu open.
children*ReactNodeLabel, optionally with a leading icon.
value*stringChecked when it equals the group's
value.disabledbooleanShown but not selectable.
| Prop | Type | Default |
|---|---|---|
children*Label, optionally with a leading icon. | ReactNode | — |
value*Checked when it equals the group's | string | — |
disabledShown but not selectable. | boolean | — |
<DropdownMenuLabel>
Also accepts every <div> prop (none of its own).
<DropdownMenuSeparator>
* required. Every component also takes className.