Skip to content

Early previewhamur is not on npm yet. The components are being built in the open, follow along on GitHub.

MenusReact

Context Menu

A menu that appears at the pointer on right click or long press.

Rendered by React 19.3.1

Loading React…
import { ContextMenu } from '@hamur/react/context-menu'

const areaStyle = {
  padding: '32px 48px',
  border: '1px dashed var(--demo-border)',
  borderRadius: 12,
  color: 'var(--demo-muted)',
  fontSize: 14,
  userSelect: 'none',
  WebkitUserSelect: 'none',
} as const

export default function ContextMenuExample() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger style={areaStyle}>
        Right click here
      </ContextMenu.Trigger>
      <ContextMenu.Portal>
        <ContextMenu.Positioner>
          <ContextMenu.Popup className="menu">
            <ContextMenu.Item className="menu-item">
              Back
            </ContextMenu.Item>
            <ContextMenu.Item className="menu-item">
              Forward
            </ContextMenu.Item>
            <ContextMenu.Item className="menu-item">
              Reload
            </ContextMenu.Item>
            <ContextMenu.Item className="menu-item">
              Save as...
            </ContextMenu.Item>
          </ContextMenu.Popup>
        </ContextMenu.Positioner>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  )
}
79Tests passing in React · 0 failing
71.6 KiBSize
5.00 msOpen
14.3 msClose
21.0 msOpenMutations
20.0 msCloseMutations
1.13 MiBMemory
productdevbook and 15 upstream authors10 commits · last

Installation

npm i @hamur/react

Not on npm yet. The package goes out under the chosen name.

Anatomy

Import the component and assemble its parts:

<ContextMenu.Root>
  <ContextMenu.Trigger />
  <ContextMenu.Portal>
    <ContextMenu.Positioner>
      <ContextMenu.Popup>
        <ContextMenu.Item />
      </ContextMenu.Popup>
    </ContextMenu.Positioner>
  </ContextMenu.Portal>
</ContextMenu.Root>

// Not in this example: ContextMenu.Arrow, ContextMenu.Backdrop, ContextMenu.CheckboxItem, ContextMenu.CheckboxItemIndicator, ContextMenu.Group, ContextMenu.GroupLabel, ContextMenu.LinkItem, ContextMenu.RadioGroup, ContextMenu.RadioItem, ContextMenu.RadioItemIndicator, ContextMenu.Separator, ContextMenu.SubmenuRoot, ContextMenu.SubmenuTrigger

Examples

Submenu

SubmenuRoot and SubmenuTrigger open a nested menu from an item.

ContextMenuSubmenu.tsx
import { ContextMenu } from '@hamur/react/context-menu'

export default function ContextMenuSubmenuExample() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger className="context-area">Right click here</ContextMenu.Trigger>
      <ContextMenu.Portal>
        <ContextMenu.Positioner>
          <ContextMenu.Popup className="menu">
            <ContextMenu.Item className="menu-item">Add to Library</ContextMenu.Item>
            <ContextMenu.SubmenuRoot>
              <ContextMenu.SubmenuTrigger className="menu-item">
                Add to Playlist
              </ContextMenu.SubmenuTrigger>
              <ContextMenu.Portal>
                <ContextMenu.Positioner alignOffset={-4} sideOffset={-4}>
                  <ContextMenu.Popup className="menu">
                    <ContextMenu.Item className="menu-item">Get Up!</ContextMenu.Item>
                    <ContextMenu.Item className="menu-item">Inside Out</ContextMenu.Item>
                    <ContextMenu.Item className="menu-item">Night Beats</ContextMenu.Item>
                    <ContextMenu.Separator className="menu-separator" />
                    <ContextMenu.Item className="menu-item">New playlist…</ContextMenu.Item>
                  </ContextMenu.Popup>
                </ContextMenu.Positioner>
              </ContextMenu.Portal>
            </ContextMenu.SubmenuRoot>
            <ContextMenu.Separator className="menu-separator" />
            <ContextMenu.Item className="menu-item">Play Next</ContextMenu.Item>
            <ContextMenu.Item className="menu-item">Play Last</ContextMenu.Item>
            <ContextMenu.Separator className="menu-separator" />
            <ContextMenu.Item className="menu-item">Favorite</ContextMenu.Item>
            <ContextMenu.Item className="menu-item">Share</ContextMenu.Item>
          </ContextMenu.Popup>
        </ContextMenu.Positioner>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  )
}

API reference

75 props across 19 parts, in React syntax. className, style, render are listed once, under Shared props.

ArrowContextMenu.Arrow

Displays an element positioned against the menu anchor. Renders a <div>.

Arrow data attributes
Data attributeDescription
data-openPresent when the menu popup is open.
data-closedPresent when the menu popup is closed.
data-sideIndicates which side the popup is positioned relative to the anchor.
data-alignIndicates how the popup is aligned relative to specified side.
data-uncenteredPresent when the menu arrow is uncentered.

BackdropContextMenu.Backdrop

An overlay displayed beneath the menu popup. Renders a <div>.

Backdrop data attributes
Data attributeDescription
data-openPresent when the menu is open.
data-closedPresent when the menu is closed.
data-starting-stylePresent when the menu begins animating in.
data-ending-stylePresent when the menu is animating out.

CheckboxItemContextMenu.CheckboxItem

A menu item that toggles a setting on or off. Renders a <div>.

CheckboxItem props
PropTypeDefault
labelOverrides the text used for keyboard text navigation and filtering.string—
defaultCheckedWhether the checkbox item is initially ticked.booleanfalse
checkedWhether the checkbox item is currently ticked.boolean—
onCheckedChangeEvent handler called when the checkbox item is ticked or unticked.((checked: boolean, eventDetails: MenuRootChangeEventDetails) => void)—
onClickThe click handler for the menu item.((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void)—
closeOnClickWhether to close the menu when the item is clicked.booleanfalse
nativeButtonWhether the component renders a native <button> element when replacing itbooleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
CheckboxItem data attributes
Data attributeDescription
data-checkedPresent when the menu checkbox item is checked.
data-uncheckedPresent when the menu checkbox item is not checked.
data-disabledPresent when the menu checkbox item is disabled.
data-highlightedPresent when the menu checkbox item is highlighted.

CheckboxItemIndicatorContextMenu.CheckboxItemIndicator

Indicates whether the checkbox item is ticked. Renders a <span>.

CheckboxItemIndicator props
PropTypeDefault
keepMountedWhether to keep the HTML element in the DOM when the checkbox item is not checked.booleanfalse
CheckboxItemIndicator data attributes
Data attributeDescription
data-checkedPresent when the menu checkbox item is checked.
data-uncheckedPresent when the menu checkbox item is not checked.
data-disabledPresent when the menu checkbox item is disabled.
data-starting-stylePresent when the indicator begins animating in.
data-ending-stylePresent when the indicator is animating out.

GroupContextMenu.Group

Groups related menu items with the corresponding label. Renders a <div>.

Group props
PropTypeDefault
childrenThe content of the component.React.ReactNode—

GroupLabelContextMenu.GroupLabel

An accessible label that is automatically associated with its parent group. Renders a <div>.

ItemContextMenu.Item

An individual interactive item in the menu. Renders a <div>.

Item props
PropTypeDefault
labelOverrides the text used for keyboard text navigation and filtering.string—
onClickThe click handler for the menu item.((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void)—
closeOnClickWhether to close the menu when the item is clicked.booleantrue
nativeButtonWhether the component renders a native <button> element when replacing itbooleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
Item data attributes
Data attributeDescription
data-highlightedPresent when the menu item is highlighted.
data-disabledPresent when the menu item is disabled.

LinkItemContextMenu.LinkItem

A link in the menu that can be used to navigate to a different page or section. Renders an <a>.

LinkItem props
PropTypeDefault
labelOverrides the text used for keyboard text navigation and filtering.string—
closeOnClickWhether to close the menu when the item is clicked.booleanfalse
LinkItem data attributes
Data attributeDescription
data-highlightedPresent when the link is highlighted.

PopupContextMenu.Popup

A container for the menu items. Renders a <div>.

Popup props
PropTypeDefault
finalFocusDetermines the element to focus when the menu is closed.boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)—
childrenReact.ReactNode—
Popup data attributes
Data attributeDescription
data-openPresent when the menu is open.
data-closedPresent when the menu is closed.
data-starting-stylePresent when the menu begins animating in.
data-ending-stylePresent when the menu is animating out.
data-sideIndicates which side the popup is positioned relative to the anchor.
data-alignIndicates how the popup is aligned relative to specified side.
data-instantPresent if animations should be instant.

PortalContextMenu.Portal

A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.

Portal props
PropTypeDefault
containerA parent element to render the portal element into.HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null—
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden.booleanfalse

PositionerContextMenu.Positioner

Positions the context menu popup against the pointer or a custom anchor. Renders a <div>.

Positioner props
PropTypeDefault
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor.booleanfalse
alignHow to align the popup relative to the specified side.Align'start'
alignOffsetAdditional offset along the alignment axis in pixels.number | OffsetFunction—
sideWhich side of the anchor element to align the popup against.Side'bottom'
sideOffsetDistance between the anchor and the popup in pixels.number | OffsetFunction—
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup.number—
anchorAn element to position the popup against.Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null—
collisionAvoidanceDetermines how to handle collisions when positioning the popup.CollisionAvoidance—
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to.Boundary'clipping-ancestors'
collisionPaddingAdditional space to maintain from the edge of the collision boundary.Padding5
stickyWhether to maintain the popup in the viewport afterbooleanfalse
Positioner data attributes
Data attributeDescription
data-openPresent when the menu popup is open.
data-closedPresent when the menu popup is closed.
data-anchor-hiddenPresent when the anchor is hidden.
data-sideIndicates which side the popup is positioned relative to the anchor.
data-alignIndicates how the popup is aligned relative to the specified side.
Positioner CSS variables
CSS variableDescription
--available-widthThe available width between the anchor and the edge of the viewport.
--available-heightThe available height between the anchor and the edge of the viewport.
--anchor-widthThe anchor's width.
--anchor-heightThe anchor's height.
--transform-originThe coordinates that this element is anchored to. Used for animations and transitions.
--positioner-widthThe width of the menu's positioner. It is important to set width to this value when using CSS to animate size changes.
--positioner-heightThe height of the menu's positioner. It is important to set height to this value when using CSS to animate size changes.

RadioGroupContextMenu.RadioGroup

Groups related radio items. Renders a <div>.

RadioGroup props
PropTypeDefault
defaultValueThe uncontrolled value of the radio item that should be initially selected.any—
valueThe controlled value of the radio item that should be currently selected.any—
onValueChangeFunction called when the selected value changes.((value: any, eventDetails: MenuRootChangeEventDetails) => void)—
disabledWhether the component should ignore user interaction.booleanfalse
childrenThe content of the component.React.ReactNode—

RadioItemContextMenu.RadioItem

A menu item that works like a radio button in a given group. Renders a <div>.

RadioItem props
PropTypeDefault
labelOverrides the text used for keyboard text navigation and filtering.string—
valuerequiredValue of the radio item.any—
onClickThe click handler for the menu item.((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void)—
closeOnClickWhether to close the menu when the item is clicked.booleanfalse
nativeButtonWhether the component renders a native <button> element when replacing itbooleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
RadioItem data attributes
Data attributeDescription
data-checkedPresent when the menu radio item is selected.
data-uncheckedPresent when the menu radio item is not selected.
data-disabledPresent when the menu radio item is disabled.
data-highlightedPresent when the menu radio item is highlighted.

RadioItemIndicatorContextMenu.RadioItemIndicator

Indicates whether the radio item is selected. Renders a <span>.

RadioItemIndicator props
PropTypeDefault
keepMountedWhether to keep the HTML element in the DOM when the radio item is inactive.booleanfalse
RadioItemIndicator data attributes
Data attributeDescription
data-checkedPresent when the menu radio item is selected.
data-uncheckedPresent when the menu radio item is not selected.
data-disabledPresent when the menu radio item is disabled.
data-starting-stylePresent when the radio indicator begins animating in.
data-ending-stylePresent when the radio indicator is animating out.

RootContextMenu.Root

A component that creates a context menu activated by right clicking or long pressing. Doesn’t render its own element.

Root props
PropTypeDefault
defaultOpenWhether the menu is initially open.booleanfalse
openWhether the menu is currently open.boolean—
onOpenChangeEvent handler called when the menu is opened or closed.((open: boolean, eventDetails: ContextMenuRootChangeEventDetails) => void)—
highlightItemOnHoverWhether moving the pointer over items should highlight them.booleantrue
actionsRefA ref to imperative actions.React.RefObject<MenuRootActions | null>—
loopFocusWhether to loop keyboard focus back to the first itembooleantrue
onItemHighlightedCallback fired when an item is highlighted or unhighlighted.((highlightedItem: HTMLElement | undefined, eventDetails: MenuRootHighlightEventDetails) => void)—
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed.((open: boolean) => void)—
disabledWhether the component should ignore user interaction.booleanfalse
orientationThe visual orientation of the menu.MenuRootOrientation'vertical'
childrenReact.ReactNode—

SeparatorContextMenu.Separator

A separator element accessible to screen readers. Renders a <div>.

Separator props
PropTypeDefault
orientationThe orientation of the separator.Orientation'horizontal'
Separator data attributes
Data attributeDescription
data-orientationIndicates the orientation of the separator.

SubmenuRootContextMenu.SubmenuRoot

Groups all parts of a submenu. Doesn’t render its own element.

SubmenuRoot props
PropTypeDefault
defaultOpenWhether the menu is initially open.booleanfalse
openWhether the menu is currently open.boolean—
onOpenChangeEvent handler called when the menu is opened or closed.((open: boolean, eventDetails: MenuRootChangeEventDetails) => void)—
highlightItemOnHoverWhether moving the pointer over items should highlight them.booleantrue
actionsRefA ref to imperative actions.React.RefObject<MenuRootActions | null>—
closeParentOnEscWhen in a submenu, determines whether pressing the Escape keybooleanfalse
loopFocusWhether to loop keyboard focus back to the first itembooleantrue
onItemHighlightedCallback fired when an item is highlighted or unhighlighted.((highlightedItem: HTMLElement | undefined, eventDetails: MenuRootHighlightEventDetails) => void)—
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed.((open: boolean) => void)—
disabledWhether the component should ignore user interaction.booleanfalse
orientationThe visual orientation of the menu.MenuRootOrientation'vertical'
childrenThe content of the submenu.React.ReactNode—

SubmenuTriggerContextMenu.SubmenuTrigger

A menu item that opens a submenu. Renders a <div>.

SubmenuTrigger props
PropTypeDefault
labelOverrides the text used for keyboard text navigation and filtering.string—
onClick((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void)—
nativeButtonWhether the component renders a native <button> element when replacing itbooleanfalse
disabledWhether the component should ignore user interaction.booleanfalse
openOnHoverWhether the menu should also open when the trigger is hovered.booleantrue
delayHow long to wait before the menu may be opened on hover.number100
closeDelayHow long to wait before closing the menu that was opened on hover.number0
SubmenuTrigger data attributes
Data attributeDescription
data-popup-openPresent when the corresponding submenu is open.
data-highlightedPresent when the submenu trigger is highlighted.
data-disabledPresent when the submenu trigger is disabled.

TriggerContextMenu.Trigger

An area that opens the menu on right click or long press. Renders a <div>.

Trigger data attributes
Data attributeDescription
data-popup-openPresent when the corresponding context menu is open.
data-pressedPresent when the corresponding context menu is open.

Shared props

Every part that renders an element takes these.

Shared props
PropType
classNameCSS class applied to the element, or a function that returns a class based on the component's state.string | ((state: State) => string | undefined)
styleStyle applied to the element, or a function that returns a style object based on the component's state.React.CSSProperties | ((state: State) => React.CSSProperties | undefined)
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State>

Keyboard and accessibility

Keyboard

Keyboard
KeysDoesPart
ShiftTabCloses the menu and returns focus to the area it was opened onPopup

Accessibility

What the parts set
PartSetsSo that
Popuprole="menu"the popup is announced as a menu
Itemrole="menuitem"each action is read as a menu item
CheckboxItemrole="menuitemcheckbox", aria-checkeda setting is read with its on or off state
RadioItemrole="menuitemradio", aria-checkedthe chosen option of a set is read
Grouprole="group", aria-labelledby to its GroupLabelrelated items are read under their label
SubmenuTriggeraria-controls to the submenua screen reader knows the item opens another menu
Separatorrole="separator"groups are divided for screen readers too
Arrowaria-hidden="true"the decorative arrow is not read

Focus order

  1. Opening moves focus into Popup.
  2. Arrow keys move between items; Tab and Shift+Tab close the menu.
  3. Closing returns focus to where it was before the menu opened.