Menu

The Mosaic Menu — the styled Mosaic component composed from the @clerk/headless menu primitive and themed with StyleX. It inherits the primitive's positioning, typeahead, roving keyboard navigation, and ARIA wiring, and adds the trigger, popup surface, and item styling.

Example

Click the trigger, then use the arrow keys or type to move between items.

Usage

import { Icon } from '@clerk/ui/mosaic/components/icon';
import { Menu } from '@clerk/ui/mosaic/components/menu';

<Menu.Root>
  <Menu.Trigger />
  <Menu.Content>
    <Menu.Item label='Add workspace'>
      <Icon name='plus' />
      Add workspace
    </Menu.Item>
    <Menu.Item
      label='Sign out'
      onClick={signOut}
    >
      <Icon name='log-out' />
      Sign out
    </Menu.Item>
    <Menu.Item
      label='Delete user'
      color='negative'
      onClick={deleteUser}
    >
      <Icon name='close' />
      Delete user
    </Menu.Item>
  </Menu.Content>
</Menu.Root>;

Menu.Content composes the portal, positioner, and popup, so items are the only children you write.

Trigger

With no children, Menu.Trigger renders a square ghost Button holding an ellipsis glyph. Pass children for a labelled trigger, or render to supply your own element — it receives the computed props (ARIA attributes, click and keyboard handlers) to spread.

<Menu.Trigger>Actions</Menu.Trigger>

<Menu.Trigger render={props => <Avatar {...props} />} />

Items

label drives typeahead and is used as the visible text when children is omitted. Render an icon and text together as children. Use color='negative' for destructive actions; the color is inherited by the children. disabled items are skipped by keyboard navigation and their onClick never fires. Activating an item closes the menu; pass closeOnClick={false} to keep it open.

<Menu.Item
  label='Delete'
  color='negative'
>
  <Icon name='close' />
  Delete
</Menu.Item>

Placement

Menu.Root takes placement and sideOffset; the popup flips and shifts automatically to stay in view, and its max-height tracks the available space so long menus scroll rather than overflow.

<Menu.Root
  placement='bottom-end'
  sideOffset={8}
>
  …
</Menu.Root>;

Controlled

const [open, setOpen] = useState(false);

<Menu.Root
  open={open}
  onOpenChange={setOpen}
>
  …
</Menu.Root>;

Parts

PartSlotDescription
Menu.RootState provider; owns open/close, placement, and keyboard navigation.
Menu.Triggermenu-triggerOpens the menu. Defaults to a square ghost Button with an ellipsis.
Menu.Contentmenu-positioner / menu-popupPortals, positions, and renders the popup surface.
Menu.Itemmenu-itemA single action whose content is composed through children.
Menu.Separatormenu-separatorFull-bleed divider between groups of items.

Styling

Unlike the slot-recipe components, the Mosaic menu is themed with StyleX. Each styled part carries a stable .cl-<slot> class (the slots above) alongside the StyleX atoms. Consumers never target the hashed atomic classes — override by targeting the .cl-* slot from a CSS layer that wins over @clerk/ui/styles.css:

@import '@clerk/ui/styles.css' layer(components);

@layer overrides {
  .cl-menu-popup {
    border-radius: 20px;
  }
}

The popup's enter/exit transition is driven off its own data-starting-style /data-ending-style attributes and is disabled under prefers-reduced-motion: reduce. Item hover state is gated behind @media (hover: hover), and the keyboard-active item is styled off data-active, so pointer and keyboard highlighting stay in sync.