Menu

Action menu built on React Aria, with sections, submenus, selection, and keyboard shortcuts.

import { Menu } from "@pockets-ui/components"
import { button } from "@/styled-system/recipes"

Anatomy

PartRole
Menu.RootWires the trigger to the popover.
Menu.TriggerThe button that opens the menu.
Menu.ContentThe popover menu. Owns selectionMode and disabledKeys.
Menu.ItemOne action, keyed by id.
Menu.ShortcutRight-aligned keyboard hint inside an item.
Menu.SectionGroups items under a heading.
Menu.SectionHeaderThat heading.
Menu.SubmenuTriggerWraps a parent item plus its nested Menu.Content.
Menu.SeparatorDivider between items or sections.

Menu.Trigger renders its own element rather than wrapping a Button, so these examples style it with the button recipe — button({ variant: "outline" }) gives the same classes the component uses.

Basic

Menu.Shortcut only displays the hint; register the actual key handler yourself.

Code
import { Menu } from "@pockets-ui/components"
import { button } from "@/styled-system/recipes"

// Menu.Trigger renders its own element, so style it with the button recipe.
const trigger = button({ variant: "outline" })

export default function MenuBasic() {
  return (
    <Menu.Root>
      <Menu.Trigger className={trigger}>Actions</Menu.Trigger>
      <Menu.Content aria-label="Actions">
        <Menu.Item id="open">
          Open <Menu.Shortcut>⌘O</Menu.Shortcut>
        </Menu.Item>
        <Menu.Item id="rename">
          Rename <Menu.Shortcut>⌘R</Menu.Shortcut>
        </Menu.Item>
        <Menu.Item id="duplicate">Duplicate</Menu.Item>
        <Menu.Separator />
        <Menu.Item id="delete">
          Delete <Menu.Shortcut>⌘⌫</Menu.Shortcut>
        </Menu.Item>
      </Menu.Content>
    </Menu.Root>
  )
}

Sections

Code
import { Menu } from "@pockets-ui/components"
import { button } from "@/styled-system/recipes"

const trigger = button({ variant: "outline" })

export default function MenuSections() {
  return (
    <Menu.Root>
      <Menu.Trigger className={trigger}>Open recent</Menu.Trigger>
      <Menu.Content aria-label="Open recent">
        <Menu.Section>
          <Menu.SectionHeader>Today</Menu.SectionHeader>
          <Menu.Item id="report">Report.pdf</Menu.Item>
          <Menu.Item id="notes">Notes.md</Menu.Item>
        </Menu.Section>
        <Menu.Separator />
        <Menu.Section>
          <Menu.SectionHeader>Last week</Menu.SectionHeader>
          <Menu.Item id="budget">Budget.xlsx</Menu.Item>
          <Menu.Item id="deck">Deck.key</Menu.Item>
        </Menu.Section>
      </Menu.Content>
    </Menu.Root>
  )
}

Single selection

With selectionMode, items become checkable state rather than one-shot actions — use it for view options, not for commands.

Code
import { Menu } from "@pockets-ui/components"
import { button } from "@/styled-system/recipes"

const trigger = button({ variant: "outline" })

export default function MenuSingleSelection() {
  return (
    <Menu.Root>
      <Menu.Trigger className={trigger}>View</Menu.Trigger>
      <Menu.Content
        aria-label="View"
        selectionMode="single"
        defaultSelectedKeys={["list"]}
      >
        <Menu.Item id="list">List</Menu.Item>
        <Menu.Item id="grid">Grid</Menu.Item>
        <Menu.Item id="columns">Columns</Menu.Item>
      </Menu.Content>
    </Menu.Root>
  )
}

Submenu

Code
import { Menu } from "@pockets-ui/components"
import { button } from "@/styled-system/recipes"

const trigger = button({ variant: "outline" })

export default function MenuSubmenu() {
  return (
    <Menu.Root>
      <Menu.Trigger className={trigger}>More</Menu.Trigger>
      <Menu.Content aria-label="More">
        <Menu.Item id="copy">Copy link</Menu.Item>
        {/* SubmenuTrigger takes the parent item and the nested Content. */}
        <Menu.SubmenuTrigger>
          <Menu.Item id="share">Share</Menu.Item>
          <Menu.Content aria-label="Share">
            <Menu.Item id="email">Email</Menu.Item>
            <Menu.Item id="message">Message</Menu.Item>
            <Menu.Item id="airdrop">AirDrop</Menu.Item>
          </Menu.Content>
        </Menu.SubmenuTrigger>
        <Menu.Separator />
        <Menu.Item id="report">Report an issue</Menu.Item>
      </Menu.Content>
    </Menu.Root>
  )
}

Disabled items

Code
import { Menu } from "@pockets-ui/components"
import { button } from "@/styled-system/recipes"

const trigger = button({ variant: "outline" })

export default function MenuDisabledItems() {
  return (
    <Menu.Root>
      <Menu.Trigger className={trigger}>Edit</Menu.Trigger>
      <Menu.Content aria-label="Edit" disabledKeys={["paste"]}>
        <Menu.Item id="cut">
          Cut <Menu.Shortcut>⌘X</Menu.Shortcut>
        </Menu.Item>
        <Menu.Item id="copy">
          Copy <Menu.Shortcut>⌘C</Menu.Shortcut>
        </Menu.Item>
        <Menu.Item id="paste">
          Paste <Menu.Shortcut>⌘V</Menu.Shortcut>
        </Menu.Item>
      </Menu.Content>
    </Menu.Root>
  )
}