{
  "title": "ContextMenu",
  "description": "Right-click (web) or long-press (native) triggered menu — for contextual actions on a target element.",
  "url": "/docs/components/context-menu",
  "since": "0.0.9",
  "tags": [
    "overlay",
    "menu"
  ],
  "platform": "both",
  "source": "---\ntitle: ContextMenu\ndescription: Right-click (web) or long-press (native) triggered menu — for contextual actions on a target element.\nsince: 0.0.9\ntags: [overlay, menu]\nplatform: both\ncategory: overlays\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { Callout } from 'fumadocs-ui/components/callout';\nimport { Preview } from '@/components/preview';\nimport { PropsTable } from '@/components/props-table';\n\n<BundleSize component=\"ContextMenu\" />\n\n## At a glance\n\n- Same compound API and menu surface as `DropdownMenu` — only the trigger gesture differs.\n- **Web**: right-click fires the `contextmenu` event. The browser's native context menu is suppressed.\n- **Native**: long-press (`onLongPress`) opens the menu.\n- Anchor: the menu is anchored to the trigger element's bounding box. True cursor-coordinate anchoring is deferred to v2.\n\n## Preview\n\n<Preview name=\"context-menu-basic\" />\n\n## Platform notes\n\n<Callout type=\"info\" title=\"Different gesture per platform\">\nThe `ContextMenu.Trigger` transparently handles both gestures. You write one JSX tree; nori-ui picks the right event per platform.\n\n```tsx\n// Works identically on web (right-click) and native (long-press)\n<ContextMenu>\n    <ContextMenu.Trigger>\n        <ImageCard />\n    </ContextMenu.Trigger>\n    <ContextMenu.Content>\n        <ContextMenu.Item onSelect={() => share()}>Share</ContextMenu.Item>\n    </ContextMenu.Content>\n</ContextMenu>\n```\n</Callout>\n\n## Anatomy\n\n| Subcomponent | Role |\n|---|---|\n| `ContextMenu` | Root — owns open state (controlled or uncontrolled). |\n| `ContextMenu.Trigger` | Element that opens the menu on right-click (web) / long-press (native). |\n| `ContextMenu.Content` | The floating menu surface. `role=\"menu\"`. |\n| `ContextMenu.Item` | Interactive menu item. `role=\"menuitem\"`. |\n| `ContextMenu.Separator` | Visual and semantic divider. `role=\"separator\"`. |\n| `ContextMenu.Label` | Non-interactive section heading. Muted small-caps. |\n\n## Open state — `open`, `defaultOpen`, `onOpenChange`\n\n```tsx\n// Uncontrolled\n<ContextMenu>\n    <ContextMenu.Trigger><View>...</View></ContextMenu.Trigger>\n    <ContextMenu.Content>...</ContextMenu.Content>\n</ContextMenu>\n\n// Controlled\nconst [open, setOpen] = useState(false);\n\n<ContextMenu open={open} onOpenChange={setOpen}>\n    <ContextMenu.Trigger><View>...</View></ContextMenu.Trigger>\n    <ContextMenu.Content>...</ContextMenu.Content>\n</ContextMenu>\n```\n\n## Accessibility\n\n- Trigger gets `aria-haspopup=\"menu\"` and `aria-expanded` automatically.\n- Content container has `role=\"menu\"`.\n- Each item has `role=\"menuitem\"` and `aria-disabled` when disabled.\n- Web keyboard navigation (same as DropdownMenu): ArrowDown/Up, Home/End, Enter/Space, Escape.\n\n## Props\n\n### ContextMenu\n\n<PropsTable component=\"ContextMenu\" />\n\n### ContextMenu.Trigger\n\nWraps the element that the user right-clicks (web) or long-presses (native) to open\nthe menu. Accepts `children` (the element to clone) and an optional `asChild` to\nrender a custom root instead of the default `View` wrapper. Adds\n`aria-haspopup=\"menu\"` and `aria-expanded` to the cloned element automatically.\n\n### ContextMenu.Content\n\nThe floating menu surface. Accepts `children` (`ContextMenu.Item`,\n`ContextMenu.Separator`, `ContextMenu.Label`) and an optional `className`.\nHas `role=\"menu\"` on web. On native it renders inside an RN `Modal` with a\ntap-outside-to-close backdrop.\n\n### ContextMenu.Item\n\n<PropsTable component=\"MenuItem\" />\n\n### ContextMenu.Separator\n\n<PropsTable component=\"MenuSeparator\" />\n\n### ContextMenu.Label\n\n<PropsTable component=\"MenuLabel\" />\n"
}
