DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Dropdown Menu

A contextual menu that opens from a trigger, built on Base UI. Supports grouped items, leading/trailing icons, keyboard shortcuts, descriptions, submenus, checkboxes, radio groups, and full RTL layout.

Playground

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>

Examples

Default

A basic menu with a label, items, and a destructive sign-out action.

Sizes

Five sizes from xs to xl, each scaling padding, text, and icons. Open each menu to compare the scale.

With Icons & Shortcuts

Items with leading icons and keyboard shortcut badges.

Checkbox Items

Toggleable checkbox items that hold their own checked state.

Radio Group

Mutually exclusive options grouped in a radio group.

Column toggles

The column-visibility menu: each row is a DropdownMenuSwitchItem — semantically a menu checkbox painted as a switch, so the menu stays open while toggling. A search field cannot live inside a role="menu" (searchbox is not an allowed menu descendant) — for a searchable list of options, reach for the Command component instead.

Header & Footer

An identity block pinned above the actions and a hint bar pinned below them — both hold their edges while a long list of items scrolls between them.

Submenu

Nested submenus for grouping related actions behind a trigger.

API Reference

DropdownMenu

The root. It owns open state and nothing visual — all Base UI Menu.Root props are forwarded.

PropTypeDefaultDescription
openboolean—Controlled open state
defaultOpenbooleanfalseUncontrolled initial state
onOpenChange(open: boolean, event, reason) => void—Fires on every open and close; reason names the dismissal ("trigger-press", "outside-press", "escape-key"…) so you can ignore specific ones
modalbooleantrueTrap focus and block the page behind the menu while it is open

DropdownMenuTrigger

The control that opens the menu. Pass asChild to merge the trigger's props onto your own element instead of rendering a button — it's a compat alias for Base UI's render, and both work.

DropdownMenuPortal

Escape hatch for placing the popup yourself. DropdownMenuContent already portals to document.body by default, so reach for this only when you need a different container and container doesn't fit.

DropdownMenuContent

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
align"start" | "center" | "end"—Alignment relative to the trigger. Unset, Base UI's own default (center) applies
sideOffsetnumber4Gap in px between trigger and panel
alignOffsetnumber0Shift in px along the side axis
collisionPaddingnumber8Minimum gap from the viewport edges before the menu flips or shifts
rounded"none" | "sm" | "md" | "lg" | "full"—Popup corner radius, propagated to every item proportionally. Left unset, the popup and its items keep radii derived from size — a deliberate exception that preserves menus written before this prop existed
dir"ltr" | "rtl"—Text direction — flips icon order, logical padding, and submenu side
positionerClassNamestring—Extra classes on the positioner — raise its z-50 when the menu sits in a higher stacking context
arrowbooleanfalseRender a speech-bubble tail pointing at the trigger
portalbooleantrueRender inside a portal at document.body
containerHTMLElement | nulldocument.bodyPortal target
inertbooleanfalseRender the popup as a static, non-interactive illustration — nothing inside can take focus
collisionAvoidanceobject—Base UI collision config — pass { side: "none" } to keep a declared side instead of flipping at a viewport edge

All other Base UI Menu.Popup props are forwarded. The popup clamps itself to the viewport: width never exceeds 100dvw - 2rem, and a menu taller than the available space scrolls instead of clipping.

DropdownMenuItem

PropTypeDefaultDescription
leadingIconReact.ReactNode—Icon on the start side
trailingIconReact.ReactNode—Icon on the end side
shortcutstring | string[]—Keyboard shortcut hint. Prefer the array of canonical names — ["mod", "K"] renders ⌘K on Apple keyboards and Ctrl+K elsewhere. A literal string ("⌘K") still works
badgeReact.ReactNode—Slot beside the label for a status pill — pass a <Badge> ("New", "Beta", a count)
descriptionstring—Secondary 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
closeOnClickbooleantruePass false to keep the menu open after the item is activated. Caveat: the press-on-trigger → drag → release-over-item gesture still closes a plain item

All native Base UI Menu.Item props are forwarded.

Portalling & collision

DropdownMenuContent and DropdownMenuSubContent also take portal (default true; false renders the popup — focus guards included — inside the component's own subtree, where it scales with transformed containers and can be covered by one inert ancestor), container (portal target), collisionAvoidance (Base UI collision config — pass { side: "none" } to keep a declared side instead of flipping away from a viewport edge), and inert (render the popup as a non-interactive illustration).

portal has a Portal toggle in the playground above — turn it off and the menu scales with the preview's zoom instead of being laid out against document.body at 100%, which is the whole reason the escape hatch exists.

container, collisionAvoidance and inert are deliberately reference-only: all three exist for embedding a menu in a harness you control — a static screenshot, a canvas that owns its own stacking context — and any example would be a picture of scaffolding rather than of the menu. Use the signatures above.

DropdownMenuLinkItem

A menu row that renders a real <a href>. Use it for every navigation item — "Open in new tab", "View on GitHub" — instead of an onClick that calls window.open: a real anchor keeps middle-click, ⌘-click, the context menu, and the status-bar URL preview. Takes the same row props as DropdownMenuItem (leadingIcon, trailingIcon, shortcut, badge, description, intent, inset) plus all <a> props (href, target, rel…).

<DropdownMenuLinkItem href="/settings" leadingIcon={<Settings />}>
  Settings
</DropdownMenuLinkItem>

<DropdownMenuLinkItem href="https://github.com/acme" target="_blank" rel="noreferrer" trailingIcon={<ExternalLink />}>
  View on GitHub
</DropdownMenuLinkItem>

closeOnClick defaults to false here (Base UI): a plain click navigates away — the page unload is the close — while a ⌘-click opens a background tab and correctly leaves the menu open. SPA route changes that do not unload the page should pass closeOnClick explicitly.

DropdownMenuSwitchItem

A toggle row painted as a switch — the column-visibility / settings-menu idiom. Semantically a Menu.CheckboxItem (role="menuitemcheckbox", announced "checked/unchecked"), so the menu stays open on toggle and keyboard behaviour is the menu's own.

PropTypeDefaultDescription
checked / onCheckedChangeboolean / (checked, eventDetails) => void—Controlled toggle state
leadingIconReact.ReactNode—Icon on the start side
descriptionstring—Secondary line below the label
disabledbooleanfalseDisables the row

The switch is presentation only — never nest a real focusable control (a <Switch>, a <button>) inside any menu item: role="menuitem*" must not contain focusable descendants, and the menu's roving tabindex would fight the inner control.

Rounded

Explicit rounded values, popup and item. Left unset, both keep radii derived from size instead (a deliberate §2 exception — see the prop table above); set it and the cascade governs everything.

ValuePopupItem
"none"rounded-nonerounded-none
"sm"rounded-mdrounded-sm
"md"rounded-lgrounded-md
"lg"rounded-xlrounded-lg
"full"rounded-3xlrounded-xl

The popup column is the same scale Popover uses — one radius vocabulary across the popup family. full was rounded-2xl (16px) until 0.3.0, which made it a reduction at md, lg and xl, whose size-derived radii are already 24, 32 and 36px.

DropdownMenuArrow

Optional macOS-style tail pointing at the trigger. Opt in with arrow on DropdownMenuContent — there is an Arrow toggle in the playground above — or place <DropdownMenuArrow/> explicitly when you want to style it. The tail's dome follows rounded and its position clears the popup's corner radius automatically.

DropdownMenuArrow, DropdownMenuShortcut and DropdownMenuPortal are exported but have no example of their own, and that is deliberate: each is rendered for you by the prop that means it — arrow on the content, shortcut on an item, and the portal the content sets up itself. The bare exports exist for the case where you are assembling a row or a popup by hand, so an example of one would be an example of the plumbing, not of a menu.

<DropdownMenuContent arrow>…</DropdownMenuContent>

DropdownMenuBackdrop

Scrim rendered under the open menu — same treatment as PopoverBackdrop. Place it as a sibling of DropdownMenuContent for mobile/modal menus that want the page dimmed while choosing.

DropdownMenuHeader / DropdownMenuFooter

Non-interactive bands pinned to the top and bottom of the menu — an account block above the actions, a hint bar or "Create new…" row below them. They stay put while the item list scrolls.

PropTypeDefaultDescription
stickybooleantruePin to the edge while items scroll
separatorbooleantrueHairline between the band and the items

DropdownMenuLabel

Section heading inside the menu. Automatically sized to match the content size. Place it inside the DropdownMenuGroup it names — the label is then announced as that group's accessible name; standalone it renders its own group.

PropTypeDefaultDescription
insetbooleanfalseAdds start padding to align with inset items

DropdownMenuSeparator

Horizontal divider between groups. No props needed.

DropdownMenuShortcut

Standalone keyboard hint, built on the design system's Shortcut/Key primitives. Used internally by DropdownMenuItem but can be composed manually.

{/* Preferred: canonical names, platform-resolved — ⌘K on Apple, Ctrl+K elsewhere */}
<DropdownMenuShortcut>{["mod", "K"]}</DropdownMenuShortcut>

{/* Literal glyph run still works (one keycap per key) */}
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
PropTypeDefaultDescription
childrenstring | string[]—Array of canonical key names (preferred) or a literal string
glyphsbooleanarray form onlyRender Apple symbols on Apple platforms
platform"mac" | "other"detectedForce a keyboard convention (previews, tests)

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 and rounded from parent context and accepts its own width and collisionPadding.

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>

DropdownMenuSkeleton

A loading placeholder for the panel. It takes its geometry from the menu's own class sources — the same contentSizeVariants and itemSizeVariants the real popup and rows use — so the swap from placeholder to content doesn't shift the layout.

PropTypeDefaultDescription
size, width, rounded—"md", "default", —Match these to the menu you're standing in for
itemsnumber6Row placeholders to render
showLabelbooleanfalseRender a label bar above the rows
showIconsbooleantrueLeading-icon placeholder on each row
showShortcutsbooleanfalseTrailing shortcut placeholder on each row

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={["mod", ","]}
  description="Manage your preferences"
>
  Settings
</DropdownMenuItem>

Destructive Items

<DropdownMenuItem intent="error" leadingIcon={<Trash />} shortcut={["mod", "backspace"]}>
  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 Base UI @base-ui/react/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)
DrawerEmptyState

On this page

PlaygroundInstallationUsageExamplesDefaultSizesWith Icons & ShortcutsCheckbox ItemsRadio GroupColumn togglesHeader & FooterSubmenuAPI ReferenceDropdownMenuDropdownMenuTriggerDropdownMenuPortalDropdownMenuContentDropdownMenuItemPortalling & collisionDropdownMenuLinkItemDropdownMenuSwitchItemRoundedDropdownMenuArrowDropdownMenuBackdropDropdownMenuHeader / DropdownMenuFooterDropdownMenuLabelDropdownMenuSeparatorDropdownMenuShortcutDropdownMenuSub / SubTrigger / SubContentDropdownMenuCheckboxItemDropdownMenuRadioGroup / RadioItemDropdownMenuSkeletonSizesWidth VariantsWith Icons, Shortcut & DescriptionDestructive ItemsRTLAccessibility