Skip to main content

Context Menu

Right-click menu with items, checkboxes, radio groups, and submenus.

import * as ContextMenu from '@gentleduck/primitives/context-menu'
import * as ContextMenu from '@gentleduck/primitives/context-menu'

Anatomy

<ContextMenu.Root>
  <ContextMenu.Trigger />
  <ContextMenu.Portal>
    <ContextMenu.Content>
      <ContextMenu.Label />
      <ContextMenu.Item />
      <ContextMenu.Group>
        <ContextMenu.Item />
      </ContextMenu.Group>
      <ContextMenu.CheckboxItem>
        <ContextMenu.ItemIndicator />
      </ContextMenu.CheckboxItem>
      <ContextMenu.RadioGroup>
        <ContextMenu.RadioItem>
          <ContextMenu.ItemIndicator />
        </ContextMenu.RadioItem>
      </ContextMenu.RadioGroup>
      <ContextMenu.Separator />
      <ContextMenu.Sub>
        <ContextMenu.SubTrigger />
        <ContextMenu.SubContent />
      </ContextMenu.Sub>
      <ContextMenu.Arrow />
    </ContextMenu.Content>
  </ContextMenu.Portal>
</ContextMenu.Root>
<ContextMenu.Root>
  <ContextMenu.Trigger />
  <ContextMenu.Portal>
    <ContextMenu.Content>
      <ContextMenu.Label />
      <ContextMenu.Item />
      <ContextMenu.Group>
        <ContextMenu.Item />
      </ContextMenu.Group>
      <ContextMenu.CheckboxItem>
        <ContextMenu.ItemIndicator />
      </ContextMenu.CheckboxItem>
      <ContextMenu.RadioGroup>
        <ContextMenu.RadioItem>
          <ContextMenu.ItemIndicator />
        </ContextMenu.RadioItem>
      </ContextMenu.RadioGroup>
      <ContextMenu.Separator />
      <ContextMenu.Sub>
        <ContextMenu.SubTrigger />
        <ContextMenu.SubContent />
      </ContextMenu.Sub>
      <ContextMenu.Arrow />
    </ContextMenu.Content>
  </ContextMenu.Portal>
</ContextMenu.Root>

Example

import * as ContextMenu from '@gentleduck/primitives/context-menu'
 
function FileExplorer() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger className="w-64 h-64 border-2 border-dashed rounded flex items-center justify-center">
        Right-click here
      </ContextMenu.Trigger>
 
      <ContextMenu.Portal>
        <ContextMenu.Content className="bg-white shadow-lg rounded-md p-1 min-w-[180px] border">
          <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
            New File
          </ContextMenu.Item>
          <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
            New Folder
          </ContextMenu.Item>
          <ContextMenu.Separator className="h-px bg-gray-200 my-1" />
          <ContextMenu.Sub>
            <ContextMenu.SubTrigger className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer flex justify-between">
              Sort by <span>></span>
            </ContextMenu.SubTrigger>
            <ContextMenu.SubContent className="bg-white shadow-lg rounded-md p-1 min-w-[140px] border">
              <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
                Name
              </ContextMenu.Item>
              <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
                Date
              </ContextMenu.Item>
            </ContextMenu.SubContent>
          </ContextMenu.Sub>
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  )
}
import * as ContextMenu from '@gentleduck/primitives/context-menu'
 
function FileExplorer() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger className="w-64 h-64 border-2 border-dashed rounded flex items-center justify-center">
        Right-click here
      </ContextMenu.Trigger>
 
      <ContextMenu.Portal>
        <ContextMenu.Content className="bg-white shadow-lg rounded-md p-1 min-w-[180px] border">
          <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
            New File
          </ContextMenu.Item>
          <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
            New Folder
          </ContextMenu.Item>
          <ContextMenu.Separator className="h-px bg-gray-200 my-1" />
          <ContextMenu.Sub>
            <ContextMenu.SubTrigger className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer flex justify-between">
              Sort by <span>></span>
            </ContextMenu.SubTrigger>
            <ContextMenu.SubContent className="bg-white shadow-lg rounded-md p-1 min-w-[140px] border">
              <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
                Name
              </ContextMenu.Item>
              <ContextMenu.Item className="px-3 py-1.5 rounded hover:bg-gray-100 cursor-pointer">
                Date
              </ContextMenu.Item>
            </ContextMenu.SubContent>
          </ContextMenu.Sub>
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu.Root>
  )
}

Key components

ContextMenu.Root

State manager. Opens on right-click on the trigger area.

PropTypeDefaultDescription
onOpenChange(open: boolean) => void--Called when open state changes
dir'ltr' | 'rtl'--Reading direction for keyboard navigation
modalbooleantrueEnable modal behavior

ContextMenu.Trigger

The area that responds to right-click. Renders a <span> by default.

ContextMenu.Content

The menu content. Positioned at the cursor location.

ContextMenu.Item

A menu item. Fires onSelect when activated.

PropTypeDescription
disabledbooleanDisable the item
onSelect(event) => voidCalled when item is selected

ContextMenu.CheckboxItem

A toggleable menu item with checked state.

PropTypeDescription
checkedboolean | 'indeterminate'Checked state
onCheckedChange(checked: boolean) => voidCalled on toggle

ContextMenu.RadioGroup / ContextMenu.RadioItem

Mutually exclusive menu items.

ContextMenu.Sub / ContextMenu.SubTrigger / ContextMenu.SubContent

Nested submenus.

ContextMenu.Separator

Visual separator between groups.

ContextMenu.Label

Non-interactive label for a group.


Keyboard interactions

KeyAction
ArrowDownHighlight next item
ArrowUpHighlight previous item
ArrowRightOpen submenu (on SubTrigger)
ArrowLeftClose submenu
Enter / SpaceActivate highlighted item
EscapeClose menu