Dropdown Menu A context menu component with items, checkboxes, radios, submenus, and keyboard navigation. Built on Radix UI.
The DropdownMenu component provides a context menu triggered by a button or other interactive element. It supports standard menu items, checkbox items, radio groups, submenus, labels, separators, and keyboard shortcuts — all built on Radix UI's DropdownMenu primitive for full accessibility.
Install the component via the CLI in one command.
npm pnpm yarn bun
npx @work-rjkashyap/unified-ui add dropdown-menu pnpm dlx @work-rjkashyap/unified-ui add dropdown-menu npx @work-rjkashyap/unified-ui add dropdown-menu bunx @work-rjkashyap/unified-ui add dropdown-menu
If you haven't initialized your project yet, run npx @work-rjkashyap/unified-ui init first. See the CLI docs for
details.
Use this approach if you prefer to install the entire design system as a dependency instead of copying individual components.
npm pnpm yarn bun
npm install @work-rjkashyap/unified-ui pnpm add @work-rjkashyap/unified-ui yarn add @work-rjkashyap/unified-ui bun add @work-rjkashyap/unified-ui
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
} from "@work-rjkashyap/unified-ui" ;
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Options</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem>Edit</DropdownMenuItem> <DropdownMenuItem>Duplicate</DropdownMenuItem> <DropdownMenuItem>Archive</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem variant="danger">Delete</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">My Account</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Account</DropdownMenuLabel> <DropdownMenuItem>Profile</DropdownMenuItem> <DropdownMenuItem>Settings</DropdownMenuItem> <DropdownMenuItem>Billing</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuLabel>Team</DropdownMenuLabel> <DropdownMenuItem>Invite Members</DropdownMenuItem> <DropdownMenuItem>Team Settings</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem variant="danger">Log Out</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Actions</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuGroup> <DropdownMenuLabel>Edit</DropdownMenuLabel> <DropdownMenuItem>Cut</DropdownMenuItem> <DropdownMenuItem>Copy</DropdownMenuItem> <DropdownMenuItem>Paste</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuLabel>View</DropdownMenuLabel> <DropdownMenuItem>Zoom In</DropdownMenuItem> <DropdownMenuItem>Zoom Out</DropdownMenuItem> <DropdownMenuItem>Reset Zoom</DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">File</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem> New File <DropdownMenuShortcut>⌘N</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> Open <DropdownMenuShortcut>⌘O</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> Save <DropdownMenuShortcut>⌘S</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem> Export <DropdownMenuShortcut>⇧⌘E</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Options</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem>New Tab</DropdownMenuItem> <DropdownMenuItem>New Window</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuSub> <DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem>Email</DropdownMenuItem> <DropdownMenuItem>Messages</DropdownMenuItem> <DropdownMenuItem>Slack</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem>Copy Link</DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuSeparator /> <DropdownMenuItem>Print</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Manage</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem>Edit</DropdownMenuItem> <DropdownMenuItem>Duplicate</DropdownMenuItem> <DropdownMenuItem>Archive</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem variant="danger">Delete</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Actions</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem>Edit</DropdownMenuItem> <DropdownMenuItem disabled>Duplicate (limit reached)</DropdownMenuItem> <DropdownMenuItem>Archive</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
Use DropdownMenuCheckboxItem for togglable menu items. Manage the checked state externally.
const [showStatusBar, setShowStatusBar] = useState(true); const [showPanel, setShowPanel] = useState(false); const [showToolbar, setShowToolbar] = useState(true); <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">View</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Appearance</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuCheckboxItem checked={showStatusBar} onCheckedChange={setShowStatusBar} > Status Bar </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked={showPanel} onCheckedChange={setShowPanel} > Side Panel </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked={showToolbar} onCheckedChange={setShowToolbar} > Toolbar </DropdownMenuCheckboxItem> </DropdownMenuContent> </DropdownMenu>
Use DropdownMenuRadioGroup and DropdownMenuRadioItem for single-select option groups.
const [theme, setTheme] = useState("system"); <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Theme</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Theme</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuRadioGroup value={theme} onValueChange={setTheme}> <DropdownMenuRadioItem value="light">Light</DropdownMenuRadioItem> <DropdownMenuRadioItem value="dark">Dark</DropdownMenuRadioItem> <DropdownMenuRadioItem value="system">System</DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuContent> </DropdownMenu>
Each DropdownMenuItem fires an onSelect callback when clicked or activated via keyboard.
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary">Actions</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem onSelect={() => navigator.clipboard.writeText(url)}> Copy Link <DropdownMenuShortcut>⌘C</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem onSelect={() => window.open(url, "_blank")}> Open in New Tab <DropdownMenuShortcut>⌘⇧T</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem variant="danger" onSelect={() => handleDelete(id)}> Delete <DropdownMenuShortcut>⌫</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
Combining all features into a comprehensive example.
<DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="secondary" iconOnly aria-label="More options"> <MoreHorizontal className="size-4" /> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuLabel>jane@example.com</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> Profile <DropdownMenuShortcut>⌘P</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> Settings <DropdownMenuShortcut>⌘,</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem>Billing</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuLabel>Preferences</DropdownMenuLabel> <DropdownMenuCheckboxItem checked={notifications} onCheckedChange={setNotifications} > Notifications </DropdownMenuCheckboxItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuSub> <DropdownMenuSubTrigger>Invite Users</DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem>Email</DropdownMenuItem> <DropdownMenuItem>Message</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem>Copy Invite Link</DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuSeparator /> <DropdownMenuItem variant="danger"> Log Out <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>
Prop Type Default Description openboolean— Controlled open state. defaultOpenboolean— Default open state (uncontrolled). onOpenChange(open: boolean) => void— Callback when the open state changes. modalbooleantrueWhether the menu blocks interaction with the rest of the page.
Uses Radix UI's trigger API. Use asChild to render as your own element.
Prop Type Default Description asChildbooleanfalseMerge props onto the child element.
Prop Type Default Description side"top" | "right" | "bottom" | "left""bottom"Which side of the trigger to place the menu. align"start" | "center" | "end""start"Alignment along the side axis. sideOffsetnumber4Distance in px from the trigger. classNamestring— Additional CSS classes.
Prop Type Default Description variant"default" | "danger""default"Visual variant. Use "danger" for destructive actions. disabledbooleanfalseWhether the item is disabled. onSelect(event: Event) => void— Callback when the item is selected (click or keyboard). classNamestring— Additional CSS classes.
Prop Type Default Description checkedboolean— Controlled checked state. onCheckedChange(checked: boolean) => void— Callback when checked state changes. disabledbooleanfalseWhether the item is disabled. classNamestring— Additional CSS classes.
Prop Type Default Description valuestring— Controlled selected value. onValueChange(value: string) => void— Callback when the selection changes.
Prop Type Default Description valuestringrequired The value of this radio item. disabledbooleanfalseWhether the item is disabled. classNamestring— Additional CSS classes.
Prop Type Default Description classNamestring— Additional CSS classes.
Prop Type Default Description classNamestring— Additional CSS classes.
Prop Type Default Description classNamestring— Additional CSS classes.
Prop Type Default Description classNamestring— Additional CSS classes.
Prop Type Default Description sideOffsetnumber2Distance from the parent menu. classNamestring— Additional CSS classes.
Built on @radix-ui/react-dropdown-menu which provides full WAI-ARIA Menu
pattern compliance.
Keyboard navigation — Arrow Up / Arrow Down to navigate items. Arrow Right to open submenus. Arrow Left to close submenus. Enter / Space to select.
Type-ahead — Typing characters focuses matching items.
Escape — Closes the menu and returns focus to the trigger.
Focus management — Focus is trapped within the menu when open. Returns to the trigger on close.
The trigger renders with aria-haspopup="menu" and aria-expanded.
Menu items have role="menuitem", checkbox items have role="menuitemcheckbox", radio items have role="menuitemradio".
DropdownMenuGroup provides semantic role="group" for assistive technology.
Disabled items are excluded from keyboard navigation and marked with aria-disabled.
Focus ring is visible on keyboard navigation only (focus-visible).
The shortcut text (DropdownMenuShortcut) is purely visual — it does not create actual keyboard bindings.
Token Usage --surface-raisedMenu panel background --borderMenu border color --primaryFocused item background highlight --dangerDanger variant item text color --muted-foregroundLabel and shortcut text color --shadow-mdMenu panel elevation shadow --radius-mdMenu panel border radius --z-dropdownZ-index for the menu layer --duration-fastEnter/exit animation timing