Context Menu

Put the actions for a file, a row, or a message where the pointer already is: right-click, long-press, or Shift+F10.

Right-click a file, long-press it, or focus it and press Shift+F10.

Installation

You can add the context menu using our CLI or manually:

Dark mode ready - this component follows BoardUI semantic tokens automatically. Set up theme switching.

The CLI copies the component source (with its internal dependencies) into your project and installs any required npm packages.

npx boardui@latest add context-menu

Using a shared UI package or custom import aliases? Configure your installation paths before running the CLI. Copy-paste examples use the default imports.

Anatomy

Wrap the target and its ContextMenu in a ContextMenuTrigger. The trigger adds no box of its own, so it fits around a file tile, a table row, or a message bubble without touching the layout.

Each row is a ContextMenuItem with an optional icon, shortcut, and variant="danger" for destructive actions. Group rows with ContextMenuSection and ContextMenuSeparator. A ContextMenuSubmenu opens a nested menu, and its rows report to the same onAction.

Pass a function as the trigger's child to highlight the target while its menu is open. With triggers inside triggers, the innermost one opens, and isDisabled hands the click back to the browser's own menu.

<ContextMenuTrigger> {({ isOpen }) => ( <> <FileTile name="Q3 report.pdf" className={isOpen ? "border-accent-500" : undefined} /> <ContextMenu aria-label="File actions">…</ContextMenu> </> )}</ContextMenuTrigger>

Checkable rows

Give a section selectionMode="multiple" for toggles or selectionMode="single" for a choice of one, control it with selectedKeys and onSelectionChange, and the selected rows show a check. Toggles leave the menu open so several can change at once.

Frame 1Right-click the canvas
const [view, setView] = useState<Selection>(new Set(["grid", "snap"]));const [zoom, setZoom] = useState<Selection>(new Set(["100"])); <ContextMenuTrigger> <Canvas tabIndex={0} /> <ContextMenu aria-label="Canvas options"> <ContextMenuSection title="View" selectionMode="multiple" selectedKeys={view} onSelectionChange={setView}> <ContextMenuItem id="grid">Show grid</ContextMenuItem> <ContextMenuItem id="snap">Snap to grid</ContextMenuItem> <ContextMenuItem id="rulers">Show rulers</ContextMenuItem> </ContextMenuSection> <ContextMenuSeparator /> <ContextMenuSection title="Zoom" selectionMode="single" disallowEmptySelection selectedKeys={zoom} onSelectionChange={setZoom}> <ContextMenuItem id="50">50%</ContextMenuItem> <ContextMenuItem id="100">100%</ContextMenuItem> <ContextMenuItem id="200">200%</ContextMenuItem> </ContextMenuSection> <ContextMenuSeparator /> <ContextMenuItem id="paste" icon={RiClipboardLine} shortcut="⌘V" isDisabled>Paste</ContextMenuItem> </ContextMenu></ContextMenuTrigger>

Right-to-left

Inside DirectionProvider, the menu opens towards the left of the pointer, its chevrons turn, and submenus open on the left with Left Arrow.

import { DirectionProvider } from "@/components/foundations/direction/direction"; <DirectionProvider locale="ar"> <ContextMenuTrigger> <FileTile name="تقرير الربع الثالث.pdf" /> <ContextMenu aria-label="إجراءات الملف"> <ContextMenuItem id="open" icon={RiExternalLinkLine}>فتح</ContextMenuItem> <ContextMenuSubmenu label="نقل إلى" icon={RiFolderTransferLine}> <ContextMenuItem id="inbox" icon={RiInboxLine}>البريد الوارد</ContextMenuItem> </ContextMenuSubmenu> <ContextMenuSeparator /> <ContextMenuItem id="delete" icon={RiDeleteBinLine} variant="danger">حذف</ContextMenuItem> </ContextMenu> </ContextMenuTrigger></DirectionProvider>

Keyboard and accessibility

Right-click or a long press opens the menu at the pointer. On a focused target, Shift+F10 or the Menu key opens it at the target with the first row highlighted, so make targets focusable. Arrow keys move through the rows, typing jumps to a row, Right Arrow opens a submenu and Left Arrow closes it, Enter runs the row, and Escape closes the menu and returns focus to the target.

Like the browser's own menu, it closes when you press outside it or the page scrolls.