Context Menu
A menu component triggered by right-click on web and long press on touch devices
- Full keyboard navigation.
- Supports items, icons, images, checkboxes, groups, and more.
- Supports submenus.
- Supports modal and non-modal modes.
- Supports native menus on native platforms.
- Customizable alignment, offsets, and positioning.
- Support for native iOS and Android icons.
- Supports previews on iOS. ContextMenu displays a menu triggered by right-click on web or long-press on mobile. It supports submenus, native platform menus with iOS previews, and automatically stacks above other content.
Installation
ContextMenu is already installed in @hanzo/gui, or you can install it independently:
yarn add @hanzogui/context-menuIf you want to use native menus, add these dependencies:
yarn add @react-native-menu/menu
yarn add react-native-ios-context-menu
yarn add react-native-ios-utilities
yarn add zeego
yarn add sf-symbols-typescriptThen add the setup import at your app entry point:
import '@hanzogui/native/setup-zeego'Expo Router users: This import must run before expo-router/entry. Create an index.js at your project root that imports the setup first, then expo-router, and update your package.json main field to "index.js". See the upgrade guide for details.
Anatomy
Import all parts and piece them together.
import { ContextMenu } from '@hanzo/gui' // or '@hanzogui/context-menu'
export default () => (
<ContextMenu>
<ContextMenu.Trigger asChild>
<YStack>
<Text>Right Click or longPress</Text>
</YStack>
</ContextMenu.Trigger>
<ContextMenu.Portal zIndex={100}>
<ContextMenu.Content>
<ContextMenu.Item>
<ContextMenu.ItemTitle>About Notes</ContextMenu.ItemTitle>
</ContextMenu.Item>
<ContextMenu.Item>
<ContextMenu.ItemTitle>Settings</ContextMenu.ItemTitle>
</ContextMenu.Item>
{/* when title is nested inside a React element then you need to use `textValue` */}
<ContextMenu.Item textValue="Calendar">
<ContextMenu.ItemTitle>
<Text>Calendar</Text>
</ContextMenu.ItemTitle>
<ContextMenu.ItemIcon>
<Calendar color="gray" size="$1" />
</ContextMenu.ItemIcon>
</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Sub>
<ContextMenu.SubTrigger>
<ContextMenu.ItemTitle>Actions</ContextMenu.ItemTitle>
</ContextMenu.SubTrigger>
<ContextMenu.Portal zIndex={200}>
<ContextMenu.SubContent>
<ContextMenu.Label fontSize={'$1'}>Note settings</ContextMenu.Label>
<ContextMenu.Item onSelect={onSelect} key="create-note">
<ContextMenu.ItemTitle>Create note</ContextMenu.ItemTitle>
</ContextMenu.Item>
<ContextMenu.Item onSelect={onSelect} key="delete-all">
<ContextMenu.ItemTitle>Delete all notes</ContextMenu.ItemTitle>
</ContextMenu.Item>
<ContextMenu.Item onSelect={onSelect} key="sync-all">
<ContextMenu.ItemTitle>Sync notes</ContextMenu.ItemTitle>
</ContextMenu.Item>
</ContextMenu.SubContent>
</ContextMenu.Portal>
</ContextMenu.Sub>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu>
)API Reference
ContextMenu
Contains every component for the ContextMenu.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | ✓ |
| placement | Placement | - | - |
| open | boolean | - | - |
| defaultOpen | boolean | - | - |
| onOpenChange | (open: boolean, event?: { preventDefault: () => void }) => void | - | - |
| onOpenWillChange | (open: boolean) => void | - | - |
| modal | boolean | true | - |
| stayInFrame | ShiftProps | boolean | { padding: 10 } | - |
| allowFlip | FlipProps | boolean | - | - |
| offset | OffsetOptions | - | - |
| unstyled | boolean | - | - |
Allowing Native Context Menu
You can call event.preventDefault() in onOpenChange to prevent the Hanzo GUI menu from opening and allow the native browser context menu to appear instead:
<ContextMenu
onOpenChange={(open, event) => {
if (someCondition) {
// prevent Hanzo GUI menu, let native context menu show
event?.preventDefault()
}
}}
>
{/* ... */}
</ContextMenu>ContextMenu.Portal
This is necessary for the ContextMenu.
| Prop | Type | Default | Required |
|---|---|---|---|
| zIndex | number | - | - |
| children | React.ReactNode | - | ✓ |
| forceMount | true | - | - |
ContextMenu.Trigger
The ContextMenu will only be triggered when the user right-clicks or long-presses within the Trigger area.
| Prop | Type | Default | Required |
|---|---|---|---|
| action | press|longPress | longPress | - |
ContextMenu.Content
Contains the content of the ContextMenu.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | ✓ |
| loop | boolean | false | - |
| forceMount | true | - | - |
| onCloseAutoFocus | (event: Event) => void | - | - |
| onEscapeKeyDown | (event: KeyboardEvent) => void | - | - |
| onPointerDownOutside | (event: PointerEvent) => void | - | - |
| onInteractOutside | (event: Event) => void | - | - |
ContextMenu.Item
A selectable menu item that triggers an action when selected.
| Prop | Type | Default | Required |
|---|---|---|---|
| key | string | - | ✓ |
| disabled | boolean | false | - |
| destructive | boolean | - | - |
| hidden | boolean | - | - |
| onSelect | (event?: Event) => void | - | - |
| onFocus | () => void | - | - |
| onBlur | () => void | - | - |
| textValue | string | - | - |
ContextMenu.ItemTitle
Renders the title of the menu item.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | string | React.ReactNode | - | ✓ |
You can directly pass a text node to the ItemTitle. However, if you use a nested
React node like <Text>, you need to pass textValue to the <Item> so that it
works with native menus.
ContextMenu.ItemIcon
A component to render an icon. For non-native menus, you can pass an icon
component. For native menus, you can pass platform-specific icons to the
android/ios props.
On iOS, it renders the native SF Symbols icons.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | - |
| ios | object | - | - |
| android | object | - | - |
<ContextMenu.ItemIcon
ios={{
name: '0.circle.fill', // required
pointSize: 5,
weight: 'semibold',
scale: 'medium',
// can also be a color string. Requires iOS 15+
hierarchicalColor: {
dark: 'blue',
light: 'green',
},
// alternative to hierarchical color. Requires iOS 15+
paletteColors: [
{
dark: 'blue',
light: 'green',
},
],
}}
>
<CircleIcon />
</ContextMenu.ItemIcon>ContextMenu.ItemImage
A component to render an item image. For native menus, it only works on iOS. It
takes the same properties as @hanzogui/image.
ContextMenu.ItemSubtitle
A component to render a subtitle for the menu item. For native menus, it only works on iOS.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | string | - | ✓ |
ContextMenu.Group
A component that groups multiple menu items together.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | ✓ |
ContextMenu.CheckboxItem
A menu item with a checkbox that can be toggled on/off.
| Prop | Type | Default | Required |
|---|---|---|---|
| key | string | - | ✓ |
| disabled | boolean | false | - |
| destructive | boolean | - | - |
| hidden | boolean | - | - |
| onFocus | () => void | - | - |
| onBlur | () => void | - | - |
| textValue | string | - | - |
| value | 'on' | 'off' | 'mixed' | - | - |
| onValueChange | (state, prevState) => void | - | - |
| checked | boolean | - | - |
| onCheckedChange | (checked: boolean) => void | - | - |
ContextMenu.ItemIndicator
Use inside CheckboxItem or RadioItem to indicate when an item is checked.
This allows you to conditionally render a checkmark.
<ContextMenu.ItemIndicator>
<CheckmarkIcon /> {/* This does not work with the native prop. */}
</ContextMenu.ItemIndicator>| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | - |
| forceMount | true | - | - |
ContextMenu.Label
Renders a non-focusable label for a group of items. On native menus, only one label is supported per menu and submenu.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | string | - | ✓ |
| textValue | string | - | - |
ContextMenu.Arrow
Renders an arrow pointing to the trigger.
| Prop | Type | Default | Required |
|---|---|---|---|
| size | number | SizeToken | - | - |
| unstyled | boolean | - | - |
ContextMenu.Separator
Renders a visual divider between menu items. Web only.
ContextMenu.Sub
A container for nested sub-menu components.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | ✓ |
| open | boolean | - | - |
| onOpenChange | (open: boolean) => void | - | - |
ContextMenu.SubContent
Renders the content of a sub-menu. Same props as Content, excluding side and
align.
ContextMenu.SubTrigger
A menu item that opens a sub-menu on hover/focus. Accepts the same props as
Item.
ContextMenu.Preview
When the ContextMenu is visible, this renders a custom preview component. Only works with native iOS menus.
ContextMenu.Content.| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | (() => React.ReactNode) | - | ✓ |
| size | { width?: number, height?: number } | - | - |
| onPress | () => void | - | - |
| backgroundColor | string | { dark: string, light: string } | - | - |
| borderRadius | number | - | - |
| preferredCommitStyle | 'pop' | 'dismiss' | 'dismiss' | - |
<ContextMenu.Preview
// optional props:
preferredCommitStyle="pop" // or "dismiss"
backgroundColor={{
// or a color string directly
dark: 'black',
light: 'white',
}}
>
{() => <Preview />}
</ContextMenu.Preview>Last updated on