{
  "title": "DropdownMenu",
  "description": "Click-triggered floating menu anchored to a trigger element — for actions, navigation, and destructive operations.",
  "url": "/docs/components/dropdown-menu",
  "since": "0.0.9",
  "tags": [
    "overlay",
    "menu"
  ],
  "platform": "both",
  "source": "---\ntitle: DropdownMenu\ndescription: Click-triggered floating menu anchored to a trigger element — for actions, navigation, and destructive operations.\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=\"DropdownMenu\" />\n\n## At a glance\n\n- Compose: `DropdownMenu`, `DropdownMenu.Trigger`, `DropdownMenu.Content`, `DropdownMenu.Item`, `DropdownMenu.Separator`, `DropdownMenu.Label`.\n- Trigger uses `asChild` — wrap any element (Button, icon, avatar) and it becomes the activator.\n- Cross-platform: same API on web and native. On native, the content renders in an RN `<Modal>` with a tap-outside-to-close backdrop.\n- Keyboard navigation on web: ArrowDown/Up cycle items, Home/End jump to first/last, Enter/Space select, Escape closes.\n- Accessibility: trigger gets `aria-haspopup=\"menu\"` + `aria-expanded`; content gets `role=\"menu\"`; items get `role=\"menuitem\"`.\n\n## Preview\n\n<Preview name=\"dropdown-menu-basic\" />\n\n## With icons\n\n```tsx\nimport { Button, DropdownMenu } from '@nori-ui/core';\n\nexport function FileMenu() {\n    return (\n        <DropdownMenu>\n            <DropdownMenu.Trigger>\n                <Button variant=\"secondary\">File</Button>\n            </DropdownMenu.Trigger>\n            <DropdownMenu.Content>\n                <DropdownMenu.Item\n                    icon={<NewFileIcon />}\n                    shortcut=\"⌘N\"\n                    onSelect={() => console.log('new')}\n                >\n                    New File\n                </DropdownMenu.Item>\n                <DropdownMenu.Item\n                    icon={<OpenIcon />}\n                    shortcut=\"⌘O\"\n                    onSelect={() => console.log('open')}\n                >\n                    Open\n                </DropdownMenu.Item>\n            </DropdownMenu.Content>\n        </DropdownMenu>\n    );\n}\n```\n\n## With destructive items\n\nUse `destructive` on items that perform irreversible operations. The item text renders in the danger color.\n\n```tsx\n<DropdownMenu>\n    <DropdownMenu.Trigger>\n        <Button variant=\"secondary\">Actions</Button>\n    </DropdownMenu.Trigger>\n    <DropdownMenu.Content>\n        <DropdownMenu.Item onSelect={() => console.log('edit')}>Edit</DropdownMenu.Item>\n        <DropdownMenu.Separator />\n        <DropdownMenu.Item destructive onSelect={() => console.log('delete')}>Delete</DropdownMenu.Item>\n    </DropdownMenu.Content>\n</DropdownMenu>\n```\n\n## With separators and labels\n\nGroup related items with `DropdownMenu.Separator` and annotate groups with `DropdownMenu.Label`.\n\n```tsx\n<DropdownMenu>\n    <DropdownMenu.Trigger>\n        <Button>My Account</Button>\n    </DropdownMenu.Trigger>\n    <DropdownMenu.Content>\n        <DropdownMenu.Label>Account</DropdownMenu.Label>\n        <DropdownMenu.Item onSelect={() => {}}>Profile</DropdownMenu.Item>\n        <DropdownMenu.Item onSelect={() => {}}>Settings</DropdownMenu.Item>\n        <DropdownMenu.Separator />\n        <DropdownMenu.Item destructive onSelect={() => {}}>Log Out</DropdownMenu.Item>\n    </DropdownMenu.Content>\n</DropdownMenu>\n```\n\n## Anatomy\n\n| Subcomponent | Role |\n|---|---|\n| `DropdownMenu` | Root — owns open state (controlled or uncontrolled). |\n| `DropdownMenu.Trigger` | Element that toggles the menu. Uses `asChild`. |\n| `DropdownMenu.Content` | The floating menu surface. `role=\"menu\"`. |\n| `DropdownMenu.Item` | Interactive menu item. `role=\"menuitem\"`. |\n| `DropdownMenu.Separator` | Visual and semantic divider. `role=\"separator\"`. |\n| `DropdownMenu.Label` | Non-interactive section heading. Muted small-caps. |\n\n## Open state — `open`, `defaultOpen`, `onOpenChange`\n\n```tsx\n// Uncontrolled\n<DropdownMenu>\n    <DropdownMenu.Trigger><Button>Open</Button></DropdownMenu.Trigger>\n    <DropdownMenu.Content>...</DropdownMenu.Content>\n</DropdownMenu>\n\n// Controlled\nconst [open, setOpen] = useState(false);\n\n<DropdownMenu open={open} onOpenChange={setOpen}>\n    <DropdownMenu.Trigger><Button>Open</Button></DropdownMenu.Trigger>\n    <DropdownMenu.Content>...</DropdownMenu.Content>\n</DropdownMenu>\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: ArrowDown/Up cycles items; Home/End jumps to bounds; Enter/Space selects; Escape closes.\n- Disabled items are skipped by keyboard nav (negative `tabIndex`).\n\n## Props\n\n### DropdownMenu\n\n<PropsTable component=\"DropdownMenu\" />\n\n### DropdownMenu.Trigger\n\nRenders the element that opens the menu. Uses `asChild` — any element passed as\n`children` is cloned and becomes the activator. The most common child is a\n`Button`. Adds `aria-haspopup=\"menu\"` and `aria-expanded` to the child\nautomatically.\n\n### DropdownMenu.Content\n\nThe floating menu surface. Accepts `children` (`DropdownMenu.Item`,\n`DropdownMenu.Separator`, `DropdownMenu.Label`) and an optional `className`.\nHas `role=\"menu\"`. On native it renders inside an RN `Modal` with a\ntap-outside-to-close backdrop.\n\n### DropdownMenu.Item\n\n<PropsTable component=\"MenuItem\" />\n\n### DropdownMenu.Separator\n\n<PropsTable component=\"MenuSeparator\" />\n\n### DropdownMenu.Label\n\n<PropsTable component=\"MenuLabel\" />\n"
}
