sc1m/design
ComponentsMenu

Menu

Menu component built on Base UI, styled with sc1m tokens.

StableBase UI

Live preview

Installation

pnpm dlx shadcn@latest add @sc1m/menu

This also merges the token layer and installs src/lib/cn.ts plus @base-ui/react. For the manual route — prerequisites, components.json, and app-level setup — see Installation.

Usage

import {
  Menu,
  MenuTrigger,
  MenuContent,
  MenuItem,
  MenuCheckboxItem,
  MenuCheckboxItemIndicator,
  MenuRadioGroup,
  MenuRadioItem,
  MenuRadioItemIndicator,
  MenuSeparator,
  MenuGroup,
  MenuGroupLabel,
  MenuSubmenuRoot,
  MenuSubmenuTrigger,
} from "@/components/ui/menu";

Composition

Every part is a separate export. Menu owns state; the rest are presentational and must appear in this order.

Menu├── MenuTrigger└── MenuContent    ├── MenuItem    └── MenuSeparator

Example

The preview above, with its source. This is the demo module verbatim, so the code and the thing it renders cannot drift apart.

<Menu>
  <MenuTrigger render={<Button variant="secondary" />}>
    Open menu
  </MenuTrigger>
  <MenuContent>
    <MenuItem>New file</MenuItem>
    <MenuItem>New window</MenuItem>
    <MenuSeparator />
    <MenuItem>Rename</MenuItem>
    <MenuItem>Duplicate</MenuItem>
    <MenuSeparator />
    <MenuItem disabled>Delete</MenuItem>
  </MenuContent>
</Menu>

API reference

Each part forwards every prop of its Base UI counterpart; the tables list the ones declared on the part itself. See Base UI's reference for the inherited element props.

PropTypeDefaultDescription
defaultOpenbooleanfalseWhether the menu is initially open.
loopFocusbooleantrueWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys.
highlightItemOnHoverbooleantrueWhether moving the pointer over items should highlight them.
modalbooleantrueDetermines if the menu enters a modal state when open.
onOpenChange((open: boolean, eventDetails: MenuRoot.ChangeEventDetails) => void)Event handler called when the menu is opened or closed.
onOpenChangeComplete((open: boolean) => void)Event handler called after any animations complete when the menu is opened or closed.
openbooleanWhether the menu is currently open.
orientationMenuRoot.Orientation'vertical'The visual orientation of the menu.
disabledbooleanfalseWhether the component should ignore user interaction.
closeParentOnEscbooleanfalseWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu.
actionsRefReact.RefObject<MenuRoot.Actions | null>A ref to imperative actions.
triggerIdstring | nullID of the trigger that the menu is associated with.
defaultTriggerIdstring | nullID of the trigger that the menu is associated with.
handleMenuHandle<Payload>A handle to associate the menu with a trigger.
PropTypeDefaultDescription
disabledbooleanfalseWhether the component should ignore user interaction.
handleMenuHandle<Payload>A handle to associate the trigger with a menu.
payloadPayloadA payload to pass to the menu when it is opened.
delaynumber100How long to wait before the menu may be opened on hover.
closeDelaynumber0How long to wait before closing the menu that was opened on hover.
openOnHoverbooleanWhether the menu should also open when the trigger is hovered.
PropTypeDefaultDescription
idstring
finalFocusboolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | HTMLElement | null | void)Determines the element to focus when the menu is closed.
PropTypeDefaultDescription
onClickBaseUIComponentProps<'div', MenuItemState>['onClick']The click handler for the menu item.
disabledbooleanfalseWhether the component should ignore user interaction.
labelstringOverrides the text label to use when the item is matched during keyboard text navigation.
idstring
closeOnClickbooleantrueWhether to close the menu when the item is clicked.
PropTypeDefaultDescription
checkedbooleanWhether the checkbox item is currently ticked.
defaultCheckedbooleanfalseWhether the checkbox item is initially ticked.
onCheckedChange((checked: boolean, eventDetails: MenuCheckboxItem.ChangeEventDetails) => void)Event handler called when the checkbox item is ticked or unticked.
onClickBaseUIComponentProps<'div', MenuCheckboxItemState>['onClick']The click handler for the menu item.
disabledbooleanfalseWhether the component should ignore user interaction.
labelstringOverrides the text label to use when the item is matched during keyboard text navigation.
idstring
closeOnClickbooleanfalseWhether to close the menu when the item is clicked.
PropTypeDefaultDescription
keepMountedbooleanfalseWhether to keep the HTML element in the DOM when the checkbox item is not checked.
PropTypeDefaultDescription
valueanyThe controlled value of the radio item that should be currently selected.
defaultValueanyThe uncontrolled value of the radio item that should be initially selected.
onValueChange((value: any, eventDetails: MenuRadioGroup.ChangeEventDetails) => void)Function called when the selected value changes.
disabledbooleanfalseWhether the component should ignore user interaction.
PropTypeDefaultDescription
valueanyValue of the radio item.
onClickBaseUIComponentProps<'div', MenuRadioItemState>['onClick']The click handler for the menu item.
disabledbooleanfalseWhether the component should ignore user interaction.
labelstringOverrides the text label to use when the item is matched during keyboard text navigation.
idstring
closeOnClickbooleanfalseWhether to close the menu when the item is clicked.
PropTypeDefaultDescription
keepMountedbooleanfalseWhether to keep the HTML element in the DOM when the radio item is inactive.
PropTypeDefaultDescription
onOpenChange((open: boolean, eventDetails: MenuSubmenuRoot.ChangeEventDetails) => void)Event handler called when the menu is opened or closed.
closeParentOnEscbooleanfalseWhen in a submenu, determines whether pressing the Escape key closes the entire menu, or only the current child menu.
PropTypeDefaultDescription
onClickBaseUIComponentProps<'div', MenuSubmenuTriggerState>['onClick']
labelstringOverrides the text label to use when the item is matched during keyboard text navigation.
idstring
disabledbooleanfalseWhether the component should ignore user interaction.
delaynumber100How long to wait before the menu may be opened on hover.
closeDelaynumber0How long to wait before closing the menu that was opened on hover.
openOnHoverbooleanWhether the menu should also open when the trigger is hovered.

Accessibility

Behaviour, focus management, and ARIA wiring come from Base UI's Menu — see its reference for the full keyboard map.

  • Decorative glyphs are aria-hidden, so they are not announced.
  • Disabled parts mute their contents rather than the element, keeping the focus ring at full strength where the part stays focusable.

Design tokens

The component reads semantic tokens only — no raw colour, radius, or duration appears in its source. Override the token, not the component.

TokenApplied to
--brand-radius-lgMenuContent
--brand-radius-smMenuItem, MenuCheckboxItem, MenuRadioItem, MenuSubmenuTrigger
--fgMenuContent, MenuRadioItemIndicator
--fg-subtleMenuGroupLabel, MenuSubmenuTrigger
--lineMenuContent, MenuSeparator
--shadow-overlayMenuContent
--surfaceMenuItem, MenuCheckboxItem, MenuRadioItem, MenuSubmenuTrigger
--surface-raisedMenuContent

Source

src/components/ui/menu/menu.tsx — no next/* imports, so it drops into any React 19 + Tailwind v4 app.