Components

Dropdown menu primitives for action items, links, radio groups, separators, and submenus.

Install

npx shadcn@latest add @alix/menu

Preview

Usage

<Menu>
  <MenuTrigger render={<Button variant="outline">Project actions</Button>} />
  <MenuContent align="start">
    <MenuItem onClick={() => openBrief()}>Open brief</MenuItem>
    <MenuItem onClick={() => duplicateBrief()}>Duplicate brief</MenuItem>
    <MenuSeparator />
    <MenuSubmenu>
      <MenuSubmenuTrigger>Row density</MenuSubmenuTrigger>
      <MenuContent side="inline-end" align="start" sideOffset={4}>
        <MenuRadioGroup value={density} onValueChange={setDensity}>
          <MenuRadioItem value="compact">Compact</MenuRadioItem>
          <MenuRadioItem value="comfortable">Comfortable</MenuRadioItem>
        </MenuRadioGroup>
      </MenuContent>
    </MenuSubmenu>
  </MenuContent>
</Menu>

Examples

01

Links and actions

Combine navigational items, command items, separators, and disabled options in one anchored menu.

No action yet
const [lastAction, setLastAction] = useState("No action yet");

return (
  <div className="flex flex-col items-start gap-3">
    <Menu>
      <MenuTrigger
        render={<Button variant="outline" rightIcon={<MoreHorizontal />}>Insight actions</Button>}
      />
      <MenuContent align="start" className="rounded-none">
        <MenuLinkItem href="/insights">
          <ExternalLink className="size-4" />
          Open insights hub
        </MenuLinkItem>
        <MenuLinkItem href="/services/transactions">
          <ArrowRight className="size-4" />
          View transactions services
        </MenuLinkItem>
        <MenuSeparator />
        <MenuItem onClick={() => setLastAction("Copied link")}>
          <Copy className="size-4" />
          Copy link
        </MenuItem>
        <MenuItem disabled>Request translation</MenuItem>
      </MenuContent>
    </Menu>
    <span className="text-xs text-muted-foreground">{lastAction}</span>
  </div>
);
02

Radio selection

Use a radio group inside the popup when the menu chooses one display mode rather than firing a command.

const [view, setView] = useState("leaders");

return (
  <Menu>
    <MenuTrigger
      render={<Button variant="outline" rightIcon={<Settings2 />}>People view</Button>}
    />
    <MenuContent align="start" className="rounded-none">
      <MenuRadioGroup value={view} onValueChange={setView}>
        <MenuRadioItem value="leaders">Leadership only</MenuRadioItem>
        <MenuRadioItem value="delivery">Delivery team</MenuRadioItem>
        <MenuRadioItem value="all">All contributors</MenuRadioItem>
      </MenuRadioGroup>
    </MenuContent>
  </Menu>
);
03

Toolbar overflow menu

A labelled Button can own the primary toolbar action while Menu and IconButton hold secondary report commands.

Ready
const [lastAction, setLastAction] = useState("Ready");

return (
  <div className="flex flex-col items-start gap-3">
    <div className="flex flex-wrap items-center gap-2">
      <Button variant="outline" leftIcon={<Filter />}>Filter</Button>
      <Menu>
        <MenuTrigger
          render={
            <IconButton variant="ghost" aria-label="More report actions">
              <MoreHorizontal />
            </IconButton>
          }
        />
        <MenuContent align="end" className="rounded-none">
          <MenuItem onClick={() => setLastAction("Brief duplicated")}>Duplicate brief</MenuItem>
          <MenuItem onClick={() => setLastAction("Export queued")}>Export summary</MenuItem>
          <MenuSeparator />
          <MenuItem disabled>Archive engagement</MenuItem>
        </MenuContent>
      </Menu>
    </div>
    <span className="text-xs text-muted-foreground">{lastAction}</span>
  </div>
);

When to use it

Use it for

  • Overflow commands, compact action lists, and mixed link/action menus anchored to one trigger.
  • Single-select display settings with MenuRadioGroup when the value changes immediately.
  • Submenus only when the grouping is worth the extra pointer and keyboard path.

Reach for something else

  • Choosing a form value from a short flat list — use Select instead.
  • Small contextual content with paragraphs or form controls — use Popover instead.
  • Icon-only triggers without an accessible name — use IconButton for the trigger.

Props

PropTypeDefaultDescription
Menuopen?, defaultOpen?, onOpenChange?The Base UI state root for the menu.
MenuTriggerrender: ReactElementTrigger slot rendered through Base UI's render prop, so a Button keeps its full API.
MenuContentside?, align?, sideOffset?, alignOffset?side "bottom", align "end", sideOffset 8The portalled popup surface; use side inline-end for submenu flyouts.
MenuItem / MenuLinkItem / MenuRadioItemBase UI item propsAction, anchor, and single-select item parts with the shared item styling.

See also