Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsAction Blocks

ContextMenu

A right-click / long-press menu anchored at the cursor. Built on Base UI's context-menu primitive — supports items, groups, labels, separators, sub-menus, checkbox-items, and radio-items. Compound API mirrors DropdownMenu so you can swap one for the other when the trigger gesture changes from click to right-click.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import {
  ContextMenu,
  ContextMenuTrigger,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuCheckboxItem,
  ContextMenuRadioGroup,
  ContextMenuRadioItem,
  ContextMenuSub,
  ContextMenuSubTrigger,
  ContextMenuSubContent,
} from "@tessinaui/ui";
<ContextMenu>
  <ContextMenuTrigger asChild>
    <div>Right-click anywhere in this region</div>
  </ContextMenuTrigger>

  <ContextMenuContent>
    <ContextMenuItem leadingIcon={<Copy />} shortcut="⌘C">Copy</ContextMenuItem>
    <ContextMenuItem leadingIcon={<Scissors />} shortcut="⌘X">Cut</ContextMenuItem>
    <ContextMenuItem leadingIcon={<Edit />}>Rename</ContextMenuItem>
    <ContextMenuSeparator />
    <ContextMenuItem leadingIcon={<Trash2 />} intent="error" shortcut="⌘⌫">
      Delete
    </ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

Showcase

When to use ContextMenu vs. DropdownMenu

Use ContextMenu when…Use DropdownMenu when…
The menu is invoked by right-click (or long-press on touch)The menu is invoked by clicking a button
Actions apply to whatever the user clicked on (a row, file, region)Actions are global / unrelated to a specific element
You want to keep the UI clean — no visible trigger requiredYou want a discoverable affordance (the button itself)
Examples: file explorer, code editor lines, table rowsExamples: avatar menu, settings dropdown, toolbar overflow

Discoverability tradeoff (per Nielsen Norman Group): context menus are powerful but invisible. If the action is critical, also expose it through a visible affordance — a button, a kebab icon, or a keyboard shortcut shown in a tooltip.

Anatomy

<ContextMenu>
  <ContextMenuTrigger>…</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuLabel />
    <ContextMenuItem />
    <ContextMenuCheckboxItem />
    <ContextMenuRadioGroup>
      <ContextMenuRadioItem />
    </ContextMenuRadioGroup>
    <ContextMenuSeparator />
    <ContextMenuSub>
      <ContextMenuSubTrigger />
      <ContextMenuSubContent />
    </ContextMenuSub>
  </ContextMenuContent>
</ContextMenu>

Trigger area

ContextMenuTrigger renders a <div> by default. The menu opens on right-click anywhere inside that region (or on long-press for touch). To use an existing element as the trigger, pass asChild:

<ContextMenuTrigger asChild>
  <table>
    <tbody>{/* … */}</tbody>
  </table>
</ContextMenuTrigger>

Size

ContextMenuContent accepts a size (xs | sm | md | lg | xl, default md) that scales item padding, font size, and the gap between items. Independent of rounded.

<ContextMenuContent size="sm">…</ContextMenuContent>

Rounded

Standalone corner-radius axis matching the five-value enum used by AlertDialog, HoverCard, and Popover.

ValueCSS
"none"rounded-none
"sm"rounded-md
"md" (default)rounded-lg
"lg"rounded-xl
"full"rounded-3xl
<ContextMenuContent rounded="lg">…</ContextMenuContent>

ContextMenuSubContent automatically inherits the same rounded value via context, so submenus' corners match the parent.

Width

ContextMenuContent also accepts width to bound the popup horizontally:

ValueBehaviour
"narrow"max-w-[12rem] — short menus
"default" (default)bounded only by min-w-* from the size variant
"wide"min-w-[20rem] — long captions or descriptions

Items

ContextMenuItem accepts:

PropTypeDescription
leadingIconReactNodeIcon on the start side
trailingIconReactNodeIcon on the end side (typically reserved for shortcut)
shortcutstringKeyboard shortcut badge (e.g. "⌘C") — pinned to the end
descriptionstringSecondary line below the label
intent"none" | "error""error" styles destructive items in red
insetbooleanIndent to align with siblings that have indicators
disabledbooleanGreys out and disables the item

Checkbox + radio items

<ContextMenuLabel>View</ContextMenuLabel>
<ContextMenuCheckboxItem checked={preview} onCheckedChange={setPreview}>
  Show preview
</ContextMenuCheckboxItem>

<ContextMenuLabel>Sort by</ContextMenuLabel>
<ContextMenuRadioGroup value={sort} onValueChange={setSort}>
  <ContextMenuRadioItem value="name">Name</ContextMenuRadioItem>
  <ContextMenuRadioItem value="date">Date</ContextMenuRadioItem>
</ContextMenuRadioGroup>

Sub-menus

<ContextMenuSub>
  <ContextMenuSubTrigger leadingIcon={<Share2 />}>Share</ContextMenuSubTrigger>
  <ContextMenuSubContent>
    <ContextMenuItem>Copy link</ContextMenuItem>
    <ContextMenuItem>Email</ContextMenuItem>
  </ContextMenuSubContent>
</ContextMenuSub>

The chevron in ContextMenuSubTrigger flips automatically based on the content's dir (right-pointing in LTR, left-pointing in RTL).

RTL

Pass dir="rtl" on ContextMenuContent (or wrap in a dir="rtl" container). Sub-menu chevrons flip and inset spacing follows the reading direction.

<ContextMenuContent dir="rtl">
  <ContextMenuItem leadingIcon={<Copy />}>نسخ</ContextMenuItem>
</ContextMenuContent>

Accessibility

  • Built on @base-ui/react/context-menu with W3C ARIA semantics: the popup uses role="menu", items use role="menuitem" (or menuitemcheckbox / menuitemradio).
  • Opens on right-click (contextmenu event) and long-press (touch).
  • Keyboard navigation: arrow keys move between items, Enter activates, Escape closes, sub-menus open on right-arrow (or left-arrow in RTL).
  • The trigger is announced as an actionable region. The popup's items are announced one by one as focus moves.
  • Use intent="error" for destructive items so assistive tech surfaces the semantic colour via the rendered text.
  • Always offer a non-context-menu way to invoke critical actions (toolbar button, keyboard shortcut shown in a tooltip) — context menus are invisible until invoked.

API Reference

ContextMenu (root)

Re-exports BaseContextMenu.Root from Base UI. Accepts the standard Base UI ContextMenu Root props.

ContextMenuTrigger

PropTypeDefaultDescription
asChildbooleanfalseRender the children directly via Base UI's render prop instead of wrapping in a <div>.

ContextMenuContent

PropTypeDefaultDescription
size"xs" | "sm" | "md" | "lg" | "xl""md"Item padding, font size, and gap
rounded"none" | "sm" | "md" | "lg" | "full""md"Popup corner radius (also inherited by ContextMenuSubContent)
width"narrow" | "default" | "wide""default"Content width clamp
sideOffsetnumber4Px gap between cursor anchor and popup
dir"ltr" | "rtl""ltr"Reading direction

ContextMenuItem

PropTypeDefaultDescription
leadingIconReactNode—Icon at the start
trailingIconReactNode—Icon at the end
shortcutstring—Keyboard shortcut badge
descriptionstring—Secondary line below the label
intent"none" | "error""none""error" for destructive
insetbooleanfalseIndent to align with checkbox/radio indicators

Sub-components

ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuCheckboxItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuPortal, ContextMenuGroup — all accept className and the Base UI primitive's native props.

Notes

  • Built on @base-ui/react/context-menu — Base UI's name for the same primitive Radix calls ContextMenu and shadcn ships as the ContextMenu component.
  • Most parts (Item, Group, Separator, CheckboxItem, RadioItem, SubmenuRoot, SubmenuTrigger) are reused from Base UI's Menu primitive — Base UI itself re-exports them.
  • The popup positions at the cursor on the side you'd expect: bottom-end of cursor in LTR, bottom-start in RTL.

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.

Command

Command palette / ⌘K menu — filterable, keyboard-driven action launcher with inline and modal modes, grouped items, descriptions, breadcrumbs, shortcuts, and footer key hints.

On this page

PlaygroundInstallationUsageShowcaseWhen to use ContextMenu vs. DropdownMenuAnatomyTrigger areaSizeRoundedWidthItemsCheckbox + radio itemsSub-menusRTLAccessibilityAPI ReferenceContextMenu (root)ContextMenuTriggerContextMenuContentContextMenuItemSub-componentsNotes