PettyUI/.firecrawl/kobalte.dev-docs-core-components-context-menu.md
2026-03-30 12:08:51 +07:00

28 KiB

Context Menu

Displays a menu located at the pointer, triggered by a right-click or a long-press.

Import

Copyts
import { ContextMenu } from "@kobalte/core/context-menu";
// or
import { Root, Trigger, ... } from "@kobalte/core/context-menu";
// or (deprecated)
import { ContextMenu } from "@kobalte/core";
Copyts
import { ContextMenu } from "@kobalte/core/context-menu";
// or
import { Root, Trigger, ... } from "@kobalte/core/context-menu";
// or (deprecated)
import { ContextMenu } from "@kobalte/core";

Features

  • Follows the WAI ARIA Menu design pattern.
  • Triggers with a long-press on touch devices.
  • Supports modal and non-modal modes.
  • Supports submenus.
  • Supports items, labels, groups of items.
  • Supports checkable items (single or multiple) with optional indeterminate state.
  • Support disabled items.
  • Complex item labeling support for accessibility.
  • Keyboard opening and navigation support.
  • Automatic scrolling support during keyboard navigation.
  • Typeahead to allow focusing items by typing text.
  • Optionally render a pointing arrow.
  • Focus is fully managed.

Anatomy

The context menu consists of:

  • ContextMenu: The root container for a context menu.
  • ContextMenu.Trigger: The button that toggles the menu.
  • ContextMenu.Icon: A small icon that can be displayed inside the menu trigger as a visual affordance for the fact it can be open.
  • ContextMenu.Portal: Portals its children into the body when the menu is open.
  • ContextMenu.Content: Contains the content to be rendered when the menu is open.
  • ContextMenu.Arrow: An optional arrow element to render alongside the menu content.
  • ContextMenu.Separator: Used to visually separate items in the menu.
  • ContextMenu.Group: Used to group multiple items. Use in conjunction with ContextMenu.GroupLabel to ensure good accessibility via automatic labelling.
  • ContextMenu.GroupLabel: Used to render the label of a group. It won't be focusable using arrow keys.
  • ContextMenu.Sub: Contains all the parts of a submenu.
  • ContextMenu.SubTrigger: An item that opens a submenu. Must be rendered inside ContextMenu.Sub.
  • ContextMenu.SubContent: The component that pops out when a submenu is open. Must be rendered inside ContextMenu.Sub.

The menu item consists of:

  • ContextMenu.Item: An item of the select.
  • ContextMenu.ItemLabel: An accessible label to be announced for the item.
  • ContextMenu.ItemDescription: An optional accessible description to be announced for the item.
  • ContextMenu.ItemIndicator: The visual indicator rendered when the item is checked.

The checkable menu item consists of:

  • ContextMenu.RadioGroup: Used to group multiple ContextMenu.RadioItems and manage the selection.
  • ContextMenu.RadioItem: An item that can be controlled and rendered like a radio.
  • ContextMenu.CheckboxItem: An item that can be controlled and rendered like a checkbox.
Copytsx
<ContextMenu>
	<ContextMenu.Trigger>
		<ContextMenu.Icon />
	</ContextMenu.Trigger>

	<ContextMenu.Portal>
		<ContextMenu.Content>
			<ContextMenu.Arrow />

			<ContextMenu.Item>
				<ContextMenu.ItemLabel />
				<ContextMenu.ItemDescription />
			</ContextMenu.Item>

			<ContextMenu.Group>
				<ContextMenu.GroupLabel />
				<ContextMenu.Item />
			</ContextMenu.Group>

			<ContextMenu.CheckboxItem>
				<ContextMenu.ItemIndicator />
			</ContextMenu.CheckboxItem>

			<ContextMenu.RadioGroup>
				<ContextMenu.RadioItem>
					<ContextMenu.ItemIndicator />
				</ContextMenu.RadioItem>
			</ContextMenu.RadioGroup>

			<ContextMenu.Sub>
				<ContextMenu.SubTrigger />
				<ContextMenu.Portal>
					<ContextMenu.SubContent />
				</ContextMenu.Portal>
			</ContextMenu.Sub>

			<ContextMenu.Separator />
		</ContextMenu.Content>
	</ContextMenu.Portal>
</ContextMenu>
Copytsx
<ContextMenu>
	<ContextMenu.Trigger>
		<ContextMenu.Icon />
	</ContextMenu.Trigger>

	<ContextMenu.Portal>
		<ContextMenu.Content>
			<ContextMenu.Arrow />

			<ContextMenu.Item>
				<ContextMenu.ItemLabel />
				<ContextMenu.ItemDescription />
			</ContextMenu.Item>

			<ContextMenu.Group>
				<ContextMenu.GroupLabel />
				<ContextMenu.Item />
			</ContextMenu.Group>

			<ContextMenu.CheckboxItem>
				<ContextMenu.ItemIndicator />
			</ContextMenu.CheckboxItem>

			<ContextMenu.RadioGroup>
				<ContextMenu.RadioItem>
					<ContextMenu.ItemIndicator />
				</ContextMenu.RadioItem>
			</ContextMenu.RadioGroup>

			<ContextMenu.Sub>
				<ContextMenu.SubTrigger />
				<ContextMenu.Portal>
					<ContextMenu.SubContent />
				</ContextMenu.Portal>
			</ContextMenu.Sub>

			<ContextMenu.Separator />
		</ContextMenu.Content>
	</ContextMenu.Portal>
</ContextMenu>

Example

Right click here.

index.tsxstyle.css

Copytsx
import { ContextMenu } from "@kobalte/core/context-menu";
import { createSignal } from "solid-js";
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "some-icon-library";
import "./style.css";

function App() {
  const [showGitLog, setShowGitLog] = createSignal(true);
  const [showHistory, setShowHistory] = createSignal(false);
  const [branch, setBranch] = createSignal("main");

  return (
    <ContextMenu>
      <ContextMenu.Trigger class="context-menu__trigger">
        Right click here.
      </ContextMenu.Trigger>
      <ContextMenu.Portal>
        <ContextMenu.Content class="context-menu__content">
          <ContextMenu.Item class="context-menu__item">
            Commit <div class="context-menu__item-right-slot">⌘+K</div>
          </ContextMenu.Item>
          <ContextMenu.Item class="context-menu__item">
            Push <div class="context-menu__item-right-slot">⇧+⌘+K</div>
          </ContextMenu.Item>
          <ContextMenu.Item class="context-menu__item" disabled>
            Update Project <div class="context-menu__item-right-slot">⌘+T</div>
          </ContextMenu.Item>
          <ContextMenu.Sub overlap gutter={4} shift={-8}>
            <ContextMenu.SubTrigger class="context-menu__sub-trigger">
              GitHub
              <div class="context-menu__item-right-slot">
                <ChevronRightIcon width={20} height={20} />
              </div>
            </ContextMenu.SubTrigger>
            <ContextMenu.Portal>
              <ContextMenu.SubContent class="context-menu__sub-content">
                <ContextMenu.Item class="context-menu__item">
                  Create Pull Request…
                </ContextMenu.Item>
                <ContextMenu.Item class="context-menu__item">
                  View Pull Requests
                </ContextMenu.Item>
                <ContextMenu.Item class="context-menu__item">
                  Sync Fork
                </ContextMenu.Item>
                <ContextMenu.Separator class="context-menu__separator" />
                <ContextMenu.Item class="context-menu__item">
                  Open on GitHub
                </ContextMenu.Item>
              </ContextMenu.SubContent>
            </ContextMenu.Portal>
          </ContextMenu.Sub>

          <ContextMenu.Separator class="context-menu__separator" />

          <ContextMenu.CheckboxItem
            class="context-menu__checkbox-item"
            checked={showGitLog()}
            onChange={setShowGitLog}
          >
            <ContextMenu.ItemIndicator class="context-menu__item-indicator">
              <CheckIcon />
            </ContextMenu.ItemIndicator>
            Show Git Log
          </ContextMenu.CheckboxItem>
          <ContextMenu.CheckboxItem
            class="context-menu__checkbox-item"
            checked={showHistory()}
            onChange={setShowHistory}
          >
            <ContextMenu.ItemIndicator class="context-menu__item-indicator">
              <CheckIcon />
            </ContextMenu.ItemIndicator>
            Show History
          </ContextMenu.CheckboxItem>

          <ContextMenu.Separator class="context-menu__separator" />

          <ContextMenu.Group>
            <ContextMenu.GroupLabel class="context-menu__group-label">
              Branches
            </ContextMenu.GroupLabel>
            <ContextMenu.RadioGroup value={branch()} onChange={setBranch}>
              <ContextMenu.RadioItem class="context-menu__radio-item" value="main">
                <ContextMenu.ItemIndicator class="context-menu__item-indicator">
                  <DotFilledIcon />
                </ContextMenu.ItemIndicator>
                main
              </ContextMenu.RadioItem>
              <ContextMenu.RadioItem class="context-menu__radio-item" value="develop">
                <ContextMenu.ItemIndicator class="context-menu__item-indicator">
                  <DotFilledIcon />
                </ContextMenu.ItemIndicator>
                develop
              </ContextMenu.RadioItem>
            </ContextMenu.RadioGroup>
          </ContextMenu.Group>
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu>
  );
}
Copytsx
import { ContextMenu } from "@kobalte/core/context-menu";
import { createSignal } from "solid-js";
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "some-icon-library";
import "./style.css";

function App() {
  const [showGitLog, setShowGitLog] = createSignal(true);
  const [showHistory, setShowHistory] = createSignal(false);
  const [branch, setBranch] = createSignal("main");

  return (
    <ContextMenu>
      <ContextMenu.Trigger class="context-menu__trigger">
        Right click here.
      </ContextMenu.Trigger>
      <ContextMenu.Portal>
        <ContextMenu.Content class="context-menu__content">
          <ContextMenu.Item class="context-menu__item">
            Commit <div class="context-menu__item-right-slot">⌘+K</div>
          </ContextMenu.Item>
          <ContextMenu.Item class="context-menu__item">
            Push <div class="context-menu__item-right-slot">⇧+⌘+K</div>
          </ContextMenu.Item>
          <ContextMenu.Item class="context-menu__item" disabled>
            Update Project <div class="context-menu__item-right-slot">⌘+T</div>
          </ContextMenu.Item>
          <ContextMenu.Sub overlap gutter={4} shift={-8}>
            <ContextMenu.SubTrigger class="context-menu__sub-trigger">
              GitHub
              <div class="context-menu__item-right-slot">
                <ChevronRightIcon width={20} height={20} />
              </div>
            </ContextMenu.SubTrigger>
            <ContextMenu.Portal>
              <ContextMenu.SubContent class="context-menu__sub-content">
                <ContextMenu.Item class="context-menu__item">
                  Create Pull Request…
                </ContextMenu.Item>
                <ContextMenu.Item class="context-menu__item">
                  View Pull Requests
                </ContextMenu.Item>
                <ContextMenu.Item class="context-menu__item">
                  Sync Fork
                </ContextMenu.Item>
                <ContextMenu.Separator class="context-menu__separator" />
                <ContextMenu.Item class="context-menu__item">
                  Open on GitHub
                </ContextMenu.Item>
              </ContextMenu.SubContent>
            </ContextMenu.Portal>
          </ContextMenu.Sub>

          <ContextMenu.Separator class="context-menu__separator" />

          <ContextMenu.CheckboxItem
            class="context-menu__checkbox-item"
            checked={showGitLog()}
            onChange={setShowGitLog}
          >
            <ContextMenu.ItemIndicator class="context-menu__item-indicator">
              <CheckIcon />
            </ContextMenu.ItemIndicator>
            Show Git Log
          </ContextMenu.CheckboxItem>
          <ContextMenu.CheckboxItem
            class="context-menu__checkbox-item"
            checked={showHistory()}
            onChange={setShowHistory}
          >
            <ContextMenu.ItemIndicator class="context-menu__item-indicator">
              <CheckIcon />
            </ContextMenu.ItemIndicator>
            Show History
          </ContextMenu.CheckboxItem>

          <ContextMenu.Separator class="context-menu__separator" />

          <ContextMenu.Group>
            <ContextMenu.GroupLabel class="context-menu__group-label">
              Branches
            </ContextMenu.GroupLabel>
            <ContextMenu.RadioGroup value={branch()} onChange={setBranch}>
              <ContextMenu.RadioItem class="context-menu__radio-item" value="main">
                <ContextMenu.ItemIndicator class="context-menu__item-indicator">
                  <DotFilledIcon />
                </ContextMenu.ItemIndicator>
                main
              </ContextMenu.RadioItem>
              <ContextMenu.RadioItem class="context-menu__radio-item" value="develop">
                <ContextMenu.ItemIndicator class="context-menu__item-indicator">
                  <DotFilledIcon />
                </ContextMenu.ItemIndicator>
                develop
              </ContextMenu.RadioItem>
            </ContextMenu.RadioGroup>
          </ContextMenu.Group>
        </ContextMenu.Content>
      </ContextMenu.Portal>
    </ContextMenu>
  );
}

Usage

Origin-aware animations

We expose a CSS custom property --kb-popper-content-transform-origin which can be used to animate the content from its computed origin.

Copycss
/* style.css */
.context-menu__content,
.context-menu__sub-content {
	transform-origin: var(--kb-menu-content-transform-origin);
	animation: contentHide 250ms ease-in forwards;
}

.context-menu__content[data-expanded],
.context-menu__sub-content[data-expanded] {
	animation: contentShow 250ms ease-out;
}

@keyframes contentShow {
	from {
		opacity: 0;
		transform: scale(0.96);
	}
	to {
		opacity: 1;
		transform: scale(1);
	}
}

@keyframes contentHide {
	from {
		opacity: 1;
		transform: scale(1);
	}
	to {
		opacity: 0;
		transform: scale(0.96);
	}
}
Copycss
/* style.css */
.context-menu__content,
.context-menu__sub-content {
	transform-origin: var(--kb-menu-content-transform-origin);
	animation: contentHide 250ms ease-in forwards;
}

.context-menu__content[data-expanded],
.context-menu__sub-content[data-expanded] {
	animation: contentShow 250ms ease-out;
}

@keyframes contentShow {
	from {
		opacity: 0;
		transform: scale(0.96);
	}
	to {
		opacity: 1;
		transform: scale(1);
	}
}

@keyframes contentHide {
	from {
		opacity: 1;
		transform: scale(1);
	}
	to {
		opacity: 0;
		transform: scale(0.96);
	}
}

API Reference

ContextMenu

ContextMenu is equivalent to the Root import from @kobalte/core/context-menu (and deprecated ContextMenu.Root).

Prop Description
onOpenChange (open: boolean) => void
Event handler called when the open state of the menu changes.
id string
A unique identifier for the component. The id is used to generate id attributes for nested components. If no id prop is provided, a generated id will be used.
modal boolean
Whether the menu should be the only visible content for screen readers, when set to true:
- interaction with outside elements will be disabled.
- scroll will be locked.
- focus will be locked inside the menu content.
- elements outside the menu content will not be visible for screen readers.
preventScroll boolean
Whether the scroll should be locked even if the menu is not modal.
forceMount boolean
Used to force mounting the menu (portal and content) when more control is needed. Useful when controlling animation with SolidJS animation libraries.

ContextMenu also accepts the following props to customize the placement of the ContextMenu.Content.

Prop Description
placement Placement
The placement of the menu content.
gutter number
The distance between the menu content and the trigger element. By default, it's 0 plus half of the arrow offset, if it exists.
shift number
The skidding of the menu content along the trigger element.
flip `boolean
slide boolean
Whether the menu content should slide when it overflows.
overlap boolean
Whether the menu content can overlap the trigger element when it overflows.
sameWidth boolean
Whether the menu content should have the same width as the trigger element. This will be exposed to CSS as --kb-popper-anchor-width.
fitViewport boolean
Whether the menu content should fit the viewport. If this is set to true, the menu content will have maxWidth and maxHeight set to the viewport size. This will be exposed to CSS as --kb-popper-available-width and --kb-popper-available-height.
hideWhenDetached boolean
Whether to hide the menu content when the trigger element becomes occluded.
detachedPadding number
The minimum padding in order to consider the trigger element occluded.
arrowPadding number
The minimum padding between the arrow and the menu content corner.
overflowPadding number
The minimum padding between the menu content and the viewport edge. This will be exposed to CSS as --kb-popper-overflow-padding.

ContextMenu.Trigger

Prop Description
disabled boolean
Whether the context menu trigger is disabled or not.
Data attribute Description
data-expanded Present when the menu is open.
data-closed Present when the menu is close.
data-disabled Present when the trigger is disabled.

ContextMenu.Icon, ContextMenu.Content, ContextMenu.SubTrigger and ContextMenu.SubContent share the same data-expanded attribute.

ContextMenu.Content

The popper positioner will copy the same z-index as the ContextMenu.Content.

Prop Description
onOpenAutoFocus (event: Event) => void
Event handler called when focus moves into the component after opening. It can be prevented by calling event.preventDefault.
onCloseAutoFocus (event: Event) => void
Event handler called when focus moves to the trigger after closing. It can be prevented by calling event.preventDefault.
onEscapeKeyDown (event: KeyboardEvent) => void
Event handler called when the escape key is down. It can be prevented by calling event.preventDefault.
onPointerDownOutside (event: PointerDownOutsideEvent) => void
Event handler called when a pointer event occurs outside the bounds of the component. It can be prevented by calling event.preventDefault.
onFocusOutside (event: FocusOutsideEvent) => void
Event handler called when the focus moves outside the bounds of the component. It can be prevented by calling event.preventDefault.
onInteractOutside (event: InteractOutsideEvent) => void
Event handler called when an interaction (pointer or focus event) happens outside the bounds of the component. It can be prevented by calling event.preventDefault.

ContextMenu.Arrow

Prop Description
size number
The size of the arrow.

ContextMenu.Item

Prop Description
textValue string
Optional text used for typeahead purposes. By default, the typeahead behavior will use the .textContent of the ContextMenu.ItemLabel part if provided, or fallback to the .textContent of the ContextMenu.Item. Use this when the content is complex, or you have non-textual content inside.
disabled boolean
Whether the item is disabled or not.
closeOnSelect boolean
Whether the menu should close when the item is activated.
onSelect () => void
Event handler called when the user selects an item (via mouse or keyboard).
Data attribute Description
data-disabled Present when the item is disabled.
data-highlighted Present when the item is highlighted.

ContextMenu.ItemLabel, ContextMenu.ItemDescription and ContextMenu.ItemIndicator shares the same data-attributes.

ContextMenu.ItemIndicator

Prop Description
forceMount boolean
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries.

ContextMenu.RadioGroup

Prop Description
value string
The controlled value of the menu radio item to check.
defaultValue string
The value of the menu radio item that should be checked when initially rendered. Useful when you do not need to control the state of the radio group.
onChange (value: string) => void
Event handler called when the value changes.
disabled boolean
Whether the radio group is disabled or not.

ContextMenu.RadioItem

Prop Description
value string
The value of the menu item radio.
textValue string
Optional text used for typeahead purposes. By default, the typeahead behavior will use the .textContent of the ContextMenu.ItemLabel part if provided, or fallback to the .textContent of the ContextMenu.Item. Use this when the content is complex, or you have non-textual content inside.
disabled boolean
Whether the item is disabled or not.
closeOnSelect boolean
Whether the menu should close when the item is checked.
onSelect () => void
Event handler called when the user selects an item (via mouse or keyboard).
Data attribute Description
data-disabled Present when the item is disabled.
data-checked Present when the item is checked.
data-highlighted Present when the item is highlighted.

ContextMenu.CheckboxItem

Prop Description
checked boolean
The controlled checked state of the item.
defaultChecked boolean
The default checked state when initially rendered. Useful when you do not need to control the checked state.
onChange (checked: boolean) => void
Event handler called when the checked state of the item changes.
textValue string
Optional text used for typeahead purposes. By default, the typeahead behavior will use the .textContent of the ContextMenu.ItemLabel part if provided, or fallback to the .textContent of the ContextMenu.Item. Use this when the content is complex, or you have non-textual content inside.
indeterminate boolean
Whether the item is in an indeterminate state.
disabled boolean
Whether the item is disabled or not.
closeOnSelect boolean
Whether the menu should close when the item is checked/unchecked.
onSelect () => void
Event handler called when the user selects an item (via mouse or keyboard).
Data attribute Description
data-disabled Present when the item is disabled.
data-indeterminate Present when the item is in an indeterminate state.
data-checked Present when the item is checked.
data-highlighted Present when the item is highlighted.

ContextMenu.Sub

Prop Description
open boolean
The controlled open state of the sub menu.
defaultOpen boolean
The default open state when initially rendered. Useful when you do not need to control the open state.
onOpenChange (open: boolean) => void
Event handler called when the open state of the sub menu changes.

ContextMenu.Sub also accepts the following props to customize the placement of the ContextMenu.SubContent.

Prop Description
getAnchorRect `(anchor?: HTMLElement) => AnchorRect
gutter number
The distance between the sub menu content and the trigger element. By default, it's 0 plus half of the arrow offset, if it exists.
shift number
The skidding of the sub menu content along the trigger element.
slide boolean
Whether the sub menu content should slide when it overflows.
overlap boolean
Whether the sub menu content can overlap the trigger element when it overflows.
fitViewport boolean
Whether the sub menu content should fit the viewport. If this is set to true, the sub menu content will have maxWidth and maxHeight set to the viewport size. This will be exposed to CSS as --kb-popper-available-width and --kb-popper-available-height.
hideWhenDetached boolean
Whether to hide the sub menu content when the trigger element becomes occluded.
detachedPadding number
The minimum padding in order to consider the trigger element occluded.
arrowPadding number
The minimum padding between the arrow and the sub menu content corner.
overflowPadding number
The minimum padding between the sub menu content and the viewport edge. This will be exposed to CSS as --kb-popper-overflow-padding.

ContextMenu.SubTrigger

Prop Description
textValue string
Optional text used for typeahead purposes. By default, the typeahead behavior will use the .textContent of the ContextMenu.SubTrigger. Use this when the content is complex, or you have non-textual content inside.
disabled boolean
Whether the sub menu trigger is disabled or not.
Data attribute Description
data-disabled Present when the item is disabled.
data-highlighted Present when the item is highlighted.

ContextMenu.SubContent

Prop Description
onEscapeKeyDown (event: KeyboardEvent) => void
Event handler called when the escape key is down. It can be prevented by calling event.preventDefault.
onPointerDownOutside (event: PointerDownOutsideEvent) => void
Event handler called when a pointer event occurs outside the bounds of the component. It can be prevented by calling event.preventDefault.
onFocusOutside (event: FocusOutsideEvent) => void
Event handler called when the focus moves outside the bounds of the component. It can be prevented by calling event.preventDefault.
onInteractOutside (event: InteractOutsideEvent) => void
Event handler called when an interaction (pointer or focus event) happens outside the bounds of the component. It can be prevented by calling event.preventDefault.

Rendered elements

Component Default rendered element
ContextMenu none
ContextMenu.Trigger div
ContextMenu.Icon div
ContextMenu.Portal Portal
ContextMenu.Content div
ContextMenu.Arrow div
ContextMenu.Separator hr
ContextMenu.Group div
ContextMenu.GroupLabel span
ContextMenu.Sub none
ContextMenu.SubTrigger div
ContextMenu.SubContent div
ContextMenu.Item div
ContextMenu.ItemLabel div
ContextMenu.ItemDescription div
ContextMenu.ItemIndicator div
ContextMenu.RadioGroup div
ContextMenu.RadioItem div
ContextMenu.CheckboxItem div

Accessibility

Keyboard Interactions

Key Description
Space When focus is on an item, activates the item.
Enter When focus is on an item, activates the item.
ArrowDown When focus is on an item, moves focus to the next item.
ArrowUp When focus is on an item, moves focus to the previous item.
ArrowRight / ArrowLeft When focus is on a sub menu trigger, opens or closes the submenu depending on reading direction.
Home When focus is on an item, moves focus to first item.
End When focus is on an item, moves focus to last item.
Esc Closes the context menu.

Previous←ComboboxNextDialog→