Tessera UI

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/ui

Usage

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:

Preview
Loading...

Showcase

All sizes, widths, states, and feature combinations:

Preview
Loading...

API Reference

The panel that contains menu items. Pass size and width here — they propagate automatically to all child items via React context.

PropTypeDefaultDescription
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)
sideOffsetnumber4Gap 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.

PropTypeDefaultDescription
leadingIconReact.ReactNodeIcon on the start side
trailingIconReact.ReactNodeIcon on the end side
shortcutstringKeyboard shortcut badge (e.g. "⌘K")
descriptionstringSecondary description line below the label
intent"none" | "error""none""error" applies destructive (red) styling to text and icons
insetbooleanfalseAdds start padding for alignment when some items have icons and others don't
disabledbooleanfalseDisables the item and mutes its appearance

All native Radix Item props are forwarded.

Section heading inside the menu. Automatically sized to match the content size.

PropTypeDefaultDescription
insetbooleanfalseAdds start padding to align with inset items

Horizontal divider between groups. No props needed.

Standalone keyboard badge. Used internally by DropdownMenuItem but can be composed manually.

<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>

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.

Checkbox-style item that maintains checked state:

const [checked, setChecked] = useState(false);

<DropdownMenuCheckboxItem checked={checked} onCheckedChange={setChecked}>
  Show toolbar
</DropdownMenuCheckboxItem>

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/Space activates; Escape closes
  • Tab dismisses the menu and returns focus to the trigger
  • Each item is a focusable [role="menuitem"]
  • Disabled items have [data-disabled] and pointer-events-none
  • Color contrast: all intent states meet WCAG 2.1 AA
  • Submenus open on ArrowRight (LTR) / ArrowLeft (RTL)

On this page