Dropdown Menu
A contextual menu that opens from a trigger, built on Radix UI. Supports grouped items, leading/trailing icons, keyboard shortcuts, descriptions, submenus, checkboxes, radio groups, and full RTL layout.
Installation
pnpm add @tessinaui/uiUsage
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@tessinaui/ui";
import { Button } from "@tessinaui/ui";<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Open</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem intent="error">Sign out</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Playground
Configure every property interactively:
Showcase
All sizes, widths, states, and feature combinations:
API Reference
DropdownMenuContent
The panel that contains menu items. Pass size and width here — they propagate automatically to all child items via React context.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Controls panel padding, item padding, text size, and icon size |
width | "narrow" | "default" | "wide" | "default" | Controls minimum width of the panel |
side | "top" | "right" | "bottom" | "left" | "bottom" | Preferred side to open on (Radix) |
align | "start" | "center" | "end" | "start" | Alignment relative to the trigger (Radix) |
sideOffset | number | 4 | Gap in px between trigger and panel (Radix) |
dir | "ltr" | "rtl" | — | Text direction — flips icon order and submenu side |
All other Radix Content props are forwarded.
DropdownMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
leadingIcon | React.ReactNode | — | Icon on the start side |
trailingIcon | React.ReactNode | — | Icon on the end side |
shortcut | string | — | Keyboard shortcut badge (e.g. "⌘K") |
description | string | — | Secondary description line below the label |
intent | "none" | "error" | "none" | "error" applies destructive (red) styling to text and icons |
inset | boolean | false | Adds start padding for alignment when some items have icons and others don't |
disabled | boolean | false | Disables the item and mutes its appearance |
All native Radix Item props are forwarded.
DropdownMenuLabel
Section heading inside the menu. Automatically sized to match the content size.
| Prop | Type | Default | Description |
|---|---|---|---|
inset | boolean | false | Adds start padding to align with inset items |
DropdownMenuSeparator
Horizontal divider between groups. No props needed.
DropdownMenuShortcut
Standalone keyboard badge. Used internally by DropdownMenuItem but can be composed manually.
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>DropdownMenuSub / SubTrigger / SubContent
Three-part pattern for nested submenus:
<DropdownMenuSub>
<DropdownMenuSubTrigger leadingIcon={<Share />}>Share</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>Email</DropdownMenuItem>
<DropdownMenuItem>Copy link</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>DropdownMenuSubTrigger accepts leadingIcon and inset. DropdownMenuSubContent inherits size from parent context.
DropdownMenuCheckboxItem
Checkbox-style item that maintains checked state:
const [checked, setChecked] = useState(false);
<DropdownMenuCheckboxItem checked={checked} onCheckedChange={setChecked}>
Show toolbar
</DropdownMenuCheckboxItem>DropdownMenuRadioGroup / RadioItem
Radio-group for mutually exclusive selection:
const [theme, setTheme] = useState("system");
<DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
<DropdownMenuRadioItem value="light">Light</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="dark">Dark</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="system">System</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>Sizes
All five sizes scale item padding, text size, icon size, and panel padding proportionally. Pass size once to DropdownMenuContent:
<DropdownMenuContent size="sm">
<DropdownMenuItem>Profile</DropdownMenuItem>
</DropdownMenuContent>Width Variants
{/* Compact narrow panel */}
<DropdownMenuContent width="narrow">...</DropdownMenuContent>
{/* Default width (min-w-[12rem]) */}
<DropdownMenuContent width="default">...</DropdownMenuContent>
{/* Wide panel — useful for items with descriptions */}
<DropdownMenuContent width="wide">...</DropdownMenuContent>With Icons, Shortcut & Description
<DropdownMenuItem
leadingIcon={<Settings />}
shortcut="⌘,"
description="Manage your preferences"
>
Settings
</DropdownMenuItem>Destructive Items
<DropdownMenuItem intent="error" leadingIcon={<Trash />} shortcut="⌫">
Delete
</DropdownMenuItem>RTL
Pass dir="rtl" to DropdownMenuContent. The layout, icon positions, and submenu open direction all flip automatically:
<DropdownMenuContent dir="rtl">
<DropdownMenuItem leadingIcon={<User />}>الملف الشخصي</DropdownMenuItem>
</DropdownMenuContent>Accessibility
- Built on Radix UI
@radix-ui/react-dropdown-menu— full keyboard navigation out of the box - Arrow keys move focus between items;
Enter/Spaceactivates;Escapecloses Tabdismisses the menu and returns focus to the trigger- Each item is a focusable
[role="menuitem"] - Disabled items have
[data-disabled]andpointer-events-none - Color contrast: all intent states meet WCAG 2.1 AA
- Submenus open on
ArrowRight(LTR) /ArrowLeft(RTL)
FAB
A pill-shaped floating action button with optional leading icon, label, and trailing icon
Card
A flexible surface container for grouping related content. Four variants (elevated/outlined/filled/ghost), five intents, six rounded options, five sizes, horizontal layout, interactive states, and LTR/RTL support.