Version 0.10.0

Menu

A list of actions in a dropdown, enhanced with keyboard navigation.

Usage#

import { MenuTrigger, Menu, MenuItem } from '@backstage/ui';

<MenuTrigger>
  <Button>Menu</Button>
  <Menu>
    <MenuItem id="apple">Apple</MenuItem>
    <MenuItem id="banana">Banana</MenuItem>
    <MenuItem id="blueberry">Blueberry</MenuItem>
    <MenuSeparator />
    <SubmenuTrigger>
      <MenuItem>Vegetables</MenuItem>
      <Menu>
        <MenuItem id="carrot">Carrot</MenuItem>
        <MenuItem id="tomato">Tomato</MenuItem>
        <MenuItem id="potato">Potato</MenuItem>
      </Menu>
    </SubmenuTrigger>
  </Menu>
</MenuTrigger>

Triggers#

  • MenuTrigger is a wrapper component that combines a button or other trigger element with a menu displayed in a popover.
  • SubmenuTrigger is a wrapper component that combines a MenuItem with a menu displayed in a popover.

Containers#

  • Menu is a container component that contains a list of menu items or sections.
  • MenuListBox is a container component that contains a list of menu items or sections.
  • MenuAutocomplete is a container component that contains a list of menu items or sections.
  • MenuAutocompleteListbox is a container component that contains a list of menu items or sections.

Items#

  • MenuItem is an individual interactive item in the menu.
  • MenuListBoxItem is an individual interactive item in the menu list box.

Separators#

  • MenuSeparator is a component that renders a horizontal line to separate menu items.
  • MenuSection is a component that renders a section in the menu.

API reference#

MenuTrigger accepts exactly two children: the first child should be the trigger element, and second child should be one of the menu containers containing the menu.

PropTypeDefaultResponsive
isOpen
boolean
-No
defaultOpen
boolean
-No
onOpenChange
(isOpen: boolean) => void
-No

The SubmenuTrigger accepts exactly two children: the first child should be the MenuItem which triggers opening of the submenu, and second child should be one of the menu containers containing the submenu.

PropTypeDefaultResponsive
delay
number
200No

Menu is a container component that contains a list of menu items or sections.

PropTypeDefaultResponsive
disabledKeys
Iterable<Key>
-No
selectionMode
nonesinglemultiple
-No
selectedKeys
allIterable<Key>
-No
defaultSelectedKeys
allIterable<Key>
-No
placement
bottombottom leftbottom rightbottom startbottom endtoptop lefttop righttop starttop endleftleft topleft bottomstartstart topstart bottomrightright topright bottomendend topend bottom
-No
virtualized
boolean
falseNo
maxWidth
number
-No
maxHeight
number
-No
className
string
-No
style
CSSProperties
-No

MenuListBox is a container component that contains a list of menu items or sections.

PropTypeDefaultResponsive
disabledKeys
Iterable<Key>
-No
selectionMode
nonesinglemultiple
-No
selectedKeys
allIterable<Key>
-No
defaultSelectedKeys
allIterable<Key>
-No
placement
bottombottom leftbottom rightbottom startbottom endtoptop lefttop righttop starttop endleftleft topleft bottomstartstart topstart bottomrightright topright bottomendend topend bottom
-No
virtualized
boolean
falseNo
maxWidth
number
-No
maxHeight
number
-No
className
string
-No
style
CSSProperties
-No

MenuAutocomplete is a container component that contains a list of menu items or sections.

PropTypeDefaultResponsive
placement
bottombottom leftbottom rightbottom startbottom endtoptop lefttop righttop starttop endleftleft topleft bottomstartstart topstart bottomrightright topright bottomendend topend bottom
-No
virtualized
boolean
falseNo
maxWidth
number
-No
maxHeight
number
-No
className
string
-No
style
CSSProperties
-No

MenuAutocompleteListbox is a container component that contains a list of menu items or sections.

PropTypeDefaultResponsive
placement
bottombottom leftbottom rightbottom startbottom endtoptop lefttop righttop starttop endleftleft topleft bottomstartstart topstart bottomrightright topright bottomendend topend bottom
-No
virtualized
boolean
falseNo
maxWidth
number
-No
maxHeight
number
-No
className
string
-No
style
CSSProperties
-No

MenuItem is an individual interactive item in the menu.

PropTypeDefaultResponsive
id
Key
-No
value
string
-No
textValue
string
-No
isDisabled
boolean
-No
href
string
-No
onAction
(event) => void
-No
className
string
-No
style
CSSProperties
-No

MenuListBoxItem is an individual interactive item in the menu list box.

PropTypeDefaultResponsive
id
Key
-No
value
string
-No
textValue
string
-No
isDisabled
boolean
-No
className
string
-No
style
CSSProperties
-No

MenuSection is a component that renders a section in the menu.

PropTypeDefaultResponsive
title
string
-No
className
string
-No
style
CSSProperties
-No

MenuSeparator is a component that renders a horizontal line to separate menu items.

PropTypeDefaultResponsive
className
string
-No
style
CSSProperties
-No

Examples#

Nested navigation#

You can nest menus to create a more complex navigation structure. It is important to use the placement prop to ensure the submenu is displayed in the correct position. The best practice is to use the right top placement for the submenu.

<MenuTrigger>
  <Button aria-label="Menu">Menu</Button>
  <Menu>
    <MenuItem>Edit</MenuItem>
    <MenuItem>Duplicate</MenuItem>
    <SubmenuTrigger>
      <MenuItem>Submenu</MenuItem>
      <Menu placement="right top">
        <MenuItem>Edit</MenuItem>
        <MenuItem>Duplicate</MenuItem>
        <MenuItem>Rename</MenuItem>
        <MenuSeparator />
        <MenuItem>Share</MenuItem>
        <MenuItem>Move</MenuItem>
        <MenuSeparator />
        <MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
      </Menu>
    </SubmenuTrigger>
  </Menu>
</MenuTrigger>

With icons#

You can use the iconStart prop to add an icon to the menu item.

<MenuTrigger>
  <Button aria-label="Menu">Menu</Button>
  <Menu>
    <MenuItem iconStart={<RiFileCopyLine />}>Copy</MenuItem>
    <MenuItem iconStart={<RiEdit2Line />}>Rename</MenuItem>
    <MenuItem iconStart={<RiChat1Line />}>Send feedback</MenuItem>
  </Menu>
</MenuTrigger>

You can use the href prop to add a link to the menu item. This is using our router provider under the hood to work for both internal and external links.

<MenuTrigger>
  <Button aria-label="Menu">Menu</Button>
  <Menu>
    <MenuItem href="/home">Internal link</MenuItem>
    <MenuItem href="https://www.google.com" target="_blank">
      External link
    </MenuItem>
    <MenuItem href="mailto:test@test.com">Email link</MenuItem>
  </Menu>
</MenuTrigger>

With sections#

You can use the MenuSection component to add a section to the menu.

<MenuTrigger>
  <Button aria-label="Menu">Menu</Button>
  <Menu>
    <MenuSection title="My Account">
      <MenuItem iconStart={<RiUserLine />}>Profile</MenuItem>
      <MenuItem iconStart={<RiSettingsLine />}>Settings</MenuItem>
    </MenuSection>
    <MenuSection title="Support">
      <MenuItem iconStart={<RiQuestionLine />}>Help Center</MenuItem>
      <MenuItem iconStart={<RiCustomerService2Line />}>
        Contact Support
      </MenuItem>
      <MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
    </MenuSection>
  </Menu>
</MenuTrigger>

With separators#

You can use the MenuSeparator component to add a separator to the menu.

<MenuTrigger>
  <Button aria-label="Menu">Menu</Button>
  <Menu>
    <MenuItem>Edit</MenuItem>
    <MenuItem>Duplicate</MenuItem>
    <MenuItem>Rename</MenuItem>
    <MenuSeparator />
    <MenuItem>Share</MenuItem>
    <MenuItem>Move</MenuItem>
    <MenuSeparator />
    <MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
  </Menu>
</MenuTrigger>

With autocomplete#

You can use the MenuAutocomplete component to add a autocomplete to the menu.

<MenuTrigger isOpen>
  <Button aria-label="Menu">Menu</Button>
  <MenuAutocomplete placeholder="Filter">
    <MenuItem>Create new file...</MenuItem>
    <MenuItem>Create new folder...</MenuItem>
    <MenuItem>Assign to...</MenuItem>
    <MenuItem>Assign to me</MenuItem>
    <MenuItem>Change status...</MenuItem>
    <MenuItem>Change priority...</MenuItem>
    <MenuItem>Add label...</MenuItem>
    <MenuItem>Remove label...</MenuItem>
  </MenuAutocomplete>
</MenuTrigger>

With list box#

You can use the MenuListBox component to add a list box to the menu.

const [selected, setSelected] = useState<Selection>(
  new Set(['blueberry']),
);

<Flex direction="column" gap="2" align="start">
  <Text>Selected: {Array.from(selected).join(', ')}</Text>
  <MenuTrigger>
    <Button aria-label="Menu">Menu</Button>
    <MenuAutocompleteListbox
      selectedKeys={selected}
      onSelectionChange={setSelected}
    >
      {options.map(option => (
        <MenuListBoxItem key={option.value} id={option.value}>
          {option.label}
        </MenuListBoxItem>
      ))}
    </MenuAutocompleteListbox>
  </MenuTrigger>
</Flex>

With list box with multiple selection#

You can use the MenuListBox component to add a list box to the menu. You can also use the selectionMode prop to allow multiple selection.

const [selected, setSelected] = useState<Selection>(
  new Set(['blueberry', 'cherry']),
);

<Flex direction="column" gap="2" align="start">
  <Text>Selected: {Array.from(selected).join(', ')}</Text>
  <MenuTrigger>
    <Button aria-label="Menu">Menu</Button>
    <MenuAutocompleteListbox
      selectionMode="multiple"
      selectedKeys={selected}
      onSelectionChange={setSelected}
    >
      {options.map(option => (
        <MenuListBoxItem key={option.value} id={option.value}>
          {option.label}
        </MenuListBoxItem>
      ))}
    </MenuAutocompleteListbox>
  </MenuTrigger>
</Flex>

Theming#

Our theming system is based on a mix between CSS classes, CSS variables and data attributes. If you want to customise this component, you can use one of these class names below.

  • bui-Menu
  • bui-MenuPopover
  • bui-MenuContent
  • bui-MenuSection
  • bui-MenuSectionHeader
  • bui-MenuItem
  • bui-MenuItemListBox
  • bui-MenuItemListBoxCheck
  • bui-MenuItemWrapper
  • bui-MenuItemContent
  • bui-MenuItemArrow
  • bui-MenuSeparator
  • bui-MenuSearchField
  • bui-MenuSearchFieldInput
  • bui-MenuSearchFieldClear
  • bui-MenuEmptyState

Changelog#

  • 0.9.0 - BREAKING: Changed className prop behavior to augment default styles instead of being ignored or overriding them.

Affected components:

  • Menu, MenuListBox, MenuAutocomplete, MenuAutocompleteListbox, MenuItem, MenuListBoxItem, MenuSection, MenuSeparator
  • Switch
  • Skeleton
  • FieldLabel
  • Header, HeaderToolbar
  • HeaderPage
  • Tabs, TabList, Tab, TabPanel

If you were passing custom className values to any of these components that relied on the previous behavior, you may need to adjust your styles to account for the default classes now being applied alongside your custom classes. #31496

  • 0.9.0 - Improved visual consistency of PasswordField, SearchField, and MenuAutocomplete components. #31679
  • 0.8.0 - Fix scroll jumping when opening menu in Backstage UI. #31394
  • 0.8.0 - Add new virtualized, maxWidth and maxHeight props to Menu, MenuListBox, MenuAutocomplete and MenuAutocompleteListBox to allow for virtalization of long lists inside menus. #31375
  • 0.8.0 - Using react router for internal links in the Menu component #31339
  • 0.7.2 - Using react router for internal links in the Menu component #31339
  • 0.7.0 - Breaking change We are updating the Menu component to use React Aria under the hood. The structure and all props are changing to follow React Aria's guidance. #30908
  • 0.7.0 - Updated Menu component in Backstage UI to use useId() from React Aria instead of React to support React 17. #30675
  • 0.7.0 - Updated Menu component in Backstage UI to use useId() from React Aria instead of React to support React 17. #30675
  • 0.4.0 - Add combobox option to Menu #29986
  • 0.4.0 - Improve Menu styles #29986
  • 0.3.0 - Update Menu styles #29351
  • 0.2.0 - New Menu component #29151