Marble UI
← Components

Menu / Dropdown Menu

Preview: Actions

Menu in its own popover. Use inside MenuTrigger (after the trigger button) or SubmenuTrigger.

Import

import { Menu, MenuItem, MenuSection, MenuSeparator, MenuTrigger, SubmenuTrigger } from '@marbleui/ui'

Props

<Menu>

PropTypeDefaultDescription
placementPlacement—

Also accepts React Aria props (95).

<MenuItem>

PropTypeDefaultDescription
destructiveboolean—
iconReactNode—
shortcutstring—

Also accepts React Aria props (100).

<MenuSection>

PropTypeDefaultDescription
titleReactNode—

Also accepts React Aria props (85).

<MenuSeparator>

No props of its own.

<MenuTrigger>

Re-exported from React Aria MenuTrigger; see its documentation for props.

<SubmenuTrigger>

Re-exported from React Aria SubmenuTrigger; see its documentation for props.

Accessibility

Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Collection · React Aria Header · React Aria Menu · React Aria MenuItem · React Aria MenuSection · React Aria MenuTrigger · React Aria Separator · React Aria SubmenuTrigger. Follow the primitive's docs for the full keyboard map.

Usage notes

Actions in a popover list. Place Menu inside MenuTrigger, after the trigger.

<MenuTrigger>
  <IconButton aria-label="More actions"><DotsIcon /></IconButton>
  <Menu onAction={(key) => run(key)}>
    <MenuItem id="rename">Rename</MenuItem>
    <MenuItem id="delete" destructive>Delete</MenuItem>
  </Menu>
</MenuTrigger>
  • Use onAction for commands. For persistent choices use MenuSection with selectionMode (radio/checkbox semantics).
  • Nest with SubmenuTrigger; typeahead and arrow keys work throughout.
  • For navigation lists use links, not menus.
Example source · show
import { useState } from 'react'
import type { Selection } from 'react-aria-components'
import { Button } from '../button'
import { IconButton } from '../icon-button'
import { Menu, MenuItem, MenuSection, MenuSeparator, MenuTrigger, SubmenuTrigger } from './menu'

const I = ({ d }: { d: string }) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
    <path d={d} />
  </svg>
)
const icons = {
  edit: 'M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z',
  copy: 'M8 8h12v12H8zM4 16V4h12',
  share: 'M4 12v8h16v-8M16 6l-4-4-4 4M12 2v13',
  trash: 'M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6',
  more: 'M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2M19 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2M5 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2',
}

function ViewMenu() {
  const [sort, setSort] = useState<Selection>(new Set(['updated']))
  const [show, setShow] = useState<Selection>(new Set(['assignee', 'labels']))
  return (
    <MenuTrigger>
      <Button variant="outline">View options</Button>
      <Menu>
        <MenuSection title="Sort by" selectionMode="single" selectedKeys={sort} onSelectionChange={setSort}>
          <MenuItem id="updated">Last updated</MenuItem>
          <MenuItem id="created">Created</MenuItem>
          <MenuItem id="priority">Priority</MenuItem>
        </MenuSection>
        <MenuSeparator />
        <MenuSection title="Show" selectionMode="multiple" selectedKeys={show} onSelectionChange={setShow}>
          <MenuItem id="assignee">Assignee</MenuItem>
          <MenuItem id="labels">Labels</MenuItem>
          <MenuItem id="estimate">Estimate</MenuItem>
        </MenuSection>
      </Menu>
    </MenuTrigger>
  )
}

export const styles = {
  Actions: () => (
    <MenuTrigger>
      <IconButton variant="outline" aria-label="Actions"><I d={icons.more} /></IconButton>
      <Menu>
        <MenuItem icon={<I d={icons.edit} />} shortcut="E">Rename</MenuItem>
        <MenuItem icon={<I d={icons.copy} />} shortcut="⌘D">Duplicate</MenuItem>
        <SubmenuTrigger>
          <MenuItem icon={<I d={icons.share} />}>Share</MenuItem>
          <Menu>
            <MenuItem>Copy link</MenuItem>
            <MenuItem>Email</MenuItem>
            <MenuItem>Slack</MenuItem>
          </Menu>
        </SubmenuTrigger>
        <MenuSeparator />
        <MenuItem icon={<I d={icons.trash} />} shortcut="⌫" destructive>Delete</MenuItem>
      </Menu>
    </MenuTrigger>
  ),
  Selection: ViewMenu,
}

Source: packages/ui/src/menu/menu.tsx

Catalog

Category
overlays
Priority
Foundation
Primitive
react-aria
React Aria
Menu + MenuTrigger + SubmenuTrigger
Base UI
Menu