Skip to main content

Menu

Drops a list of actions below a trigger, driveable entirely by keyboard.

import { Menu } from "@/fragments/ui";Source

Preview

<Menu>
  <Menu.Trigger asChild>
    <Button variant="soft">Actions</Button>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.Item onSelect={() => {}}>Edit</Menu.Item>

Installation

npx @usefragments/cli add menu

Writes src/fragments/ui/components/Menu plus what it imports, the globals stylesheet, and .fragments/registry-lock.json, then prints the packages to add. No account, no key.

Your own registry: publish from Fragments Cloud and add --registry org/name. Connect a workspace.

Anatomy

Menu is a compound: import the root and reach its parts through dot notation.

  • Menu.Trigger
  • Menu.Content
  • Menu.Item
  • Menu.CheckboxItem
  • Menu.RadioGroup
  • Menu.RadioItem
  • Menu.Group
  • Menu.GroupLabel
  • Menu.Separator
  • Menu.Submenu
  • Menu.SubmenuTrigger

Plain actions, one per row.

<Menu>
  <Menu.Trigger asChild>
    <Button variant="soft">Actions</Button>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.Item onSelect={() => {}}>Edit</Menu.Item>
    <Menu.Item onSelect={() => {}}>Duplicate</Menu.Item>
    <Menu.Separator />
    <Menu.Item danger onSelect={() => {}}>Delete</Menu.Item>
  </Menu.Content>
</Menu>

Examples

With Shortcuts

Trailing keys teach the faster path.

<Menu>
  <Menu.Trigger asChild>
    <Button variant="soft">Edit</Button>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.Item shortcut="Ctrl+Z" onSelect={() => {}}>Undo</Menu.Item>

With Groups

Labels separate unrelated actions.

<Menu>
  <Menu.Trigger asChild>
    <Button variant="soft">Options</Button>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.Group>

With Checkboxes

Rows that stay open while you toggle them.

<Menu>
  <Menu.Trigger asChild>
    <Button variant="soft">Display</Button>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.CheckboxItem defaultChecked>Show Grid</Menu.CheckboxItem>

With Checked Items

Ticks mark which filters are on.

<CheckedItemsMenuDemo />

With Submenu

Secondary options nest one level deep.

<Menu>
  <Menu.Trigger asChild>
    <Button variant="soft">File</Button>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.Item onSelect={() => {}}>New File</Menu.Item>

asChild lets a link open it.

<Menu>
  <Menu.Trigger asChild>
    <a href="#menu-actions">Open menu</a>
  </Menu.Trigger>
  <Menu.Content>
    <Menu.Item onSelect={() => {}}>Inspect</Menu.Item>

API

Component props
Prop
Type
Default
Description
childrenRequirednodeNot setMenu trigger and content
openbooleanNot setControlled open state
defaultOpenbooleanfalseDefault open state (uncontrolled)
onOpenChangefunctionNot setCalled when open state changes
modalbooleantrueWhether menu is modal

Accessibility

  • Full keyboard navigation with arrow keys
  • Type-ahead search for items
  • Focus returns to trigger on close
  • Proper ARIA menu roles
  • ArrowRight opens submenus, ArrowLeft closes them

Guidance

Use when

  • Actions that do not fit in the toolbar
  • Right-click context actions
  • Account and profile actions
  • Related actions that read better grouped

Avoid when

  • Picking a value for a form (use Select)
  • Moving between pages or views (use Tabs or NavigationMenu)
  • One of a few visible choices (use RadioGroup)
  • SelectalternativeUse Select for form field selection
  • PopoveralternativeUse Popover for rich non-action content

Next steps