A dropdown menu with actions, submenus, sections, and keyboard navigation.
import { MenuTrigger, Menu, MenuItem } from '@backstage/ui';
<MenuTrigger>
<Button>Menu</Button>
<Menu>
<MenuItem>Item 1</MenuItem>
<MenuItem>Item 2</MenuItem>
</Menu>
</MenuTrigger>MenuTrigger combines a trigger element with a menu popover.SubmenuTrigger combines a MenuItem with a nested submenu.Menu contains menu items or sections.MenuListBox supports selection with checkmarks.MenuAutocomplete adds a search input to filter items.MenuAutocompleteListbox combines search with selection.MenuItem is an interactive action in the menu.MenuListBoxItem is a selectable item in a list box.MenuSection groups items with a title.MenuSeparator adds a horizontal divider.Accepts two children: the trigger element and a menu container.
| Prop | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | - | Controlled open state of the menu. |
| defaultOpen | boolean | - | Whether the menu is open by default. |
| onOpenChange | (isOpen: boolean) => void | - | Handler called when the open state changes. |
Inherits all React Aria MenuTrigger props.
Accepts two children: a MenuItem and a menu container.
| Prop | Type | Default | Description |
|---|---|---|---|
| delay | number | 200 | Delay in milliseconds before the submenu opens on hover. |
Inherits all React Aria SubmenuTrigger props.
| Prop | Type | Default | Description |
|---|---|---|---|
| placement | topbottomleftrighttop starttop endbottom startbottom endleft startleft endright startright end | - | Position of the menu relative to the trigger. |
| onAction | (key: Key) => void | - | Handler called when an item is activated. Receives the item key. |
| selectionMode | nonesinglemultiple | - | How items can be selected. |
| selectedKeys | Iterable<Key> | - | Controlled selected keys. |
| defaultSelectedKeys | Iterable<Key> | - | Default selected keys for uncontrolled usage. |
| disabledKeys | Iterable<Key> | - | Keys of items that are disabled. |
| virtualized | boolean | - | Enable virtualization for large lists. |
| maxWidth | string | - | Maximum width of the menu popover. |
| maxHeight | string | - | Maximum height of the menu popover. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria Menu props.
| Prop | Type | Default | Description |
|---|---|---|---|
| placement | topbottomleftrighttop starttop endbottom startbottom endleft startleft endright startright end | - | Position of the list box relative to the trigger. |
| selectionMode | nonesinglemultiple | - | How items can be selected. |
| selectedKeys | Iterable<Key> | - | Controlled selected keys. |
| defaultSelectedKeys | Iterable<Key> | - | Default selected keys for uncontrolled usage. |
| onSelectionChange | (keys: Selection) => void | - | Handler called when selection changes. |
| disabledKeys | Iterable<Key> | - | Keys of items that are disabled. |
| virtualized | boolean | - | Enable virtualization for large lists. |
| maxWidth | string | - | Maximum width of the list box popover. |
| maxHeight | string | - | Maximum height of the list box popover. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria ListBox props.
| Prop | Type | Default | Description |
|---|---|---|---|
| placeholder | string | - | Placeholder text for the search input. |
| placement | topbottomleftrighttop starttop endbottom startbottom endleft startleft endright startright end | - | Position of the menu relative to the trigger. |
| onAction | (key: Key) => void | - | Handler called when an item is activated. Receives the item key. |
| selectionMode | nonesinglemultiple | - | How items can be selected. |
| selectedKeys | Iterable<Key> | - | Controlled selected keys. |
| defaultSelectedKeys | Iterable<Key> | - | Default selected keys for uncontrolled usage. |
| disabledKeys | Iterable<Key> | - | Keys of items that are disabled. |
| onSelectionChange | (keys: Selection) => void | - | Handler called when selection changes. |
| virtualized | boolean | - | Enable virtualization for large lists. |
| maxWidth | string | - | Maximum width of the menu popover. |
| maxHeight | string | - | Maximum height of the menu popover. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria Menu props.
| Prop | Type | Default | Description |
|---|---|---|---|
| placeholder | string | - | Placeholder text for the search input. |
| placement | topbottomleftrighttop starttop endbottom startbottom endleft startleft endright startright end | - | Position of the list box relative to the trigger. |
| selectionMode | nonesinglemultiple | - | How items can be selected. |
| selectedKeys | Iterable<Key> | - | Controlled selected keys. |
| defaultSelectedKeys | Iterable<Key> | - | Default selected keys for uncontrolled usage. |
| onSelectionChange | (keys: Selection) => void | - | Handler called when selection changes. |
| virtualized | boolean | - | Enable virtualization for large lists. |
| maxWidth | string | - | Maximum width of the list box popover. |
| maxHeight | string | - | Maximum height of the list box popover. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria ListBox props.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | - | Content displayed in the menu item. |
| id | Key | - | Unique key for the item. |
| iconStart | ReactNode | - | Icon displayed before the item content. |
| color | primarydanger | - | Color variant. Use danger for destructive actions. |
| href | string | - | URL to navigate to when the item is clicked. |
| isDisabled | boolean | - | Whether the item is disabled. |
| noTrack | boolean | - | Suppresses the automatic analytics click event, e.g. if you already have custom tracking. |
| textValue | string | - | Text used for typeahead and accessibility. |
| onAction | () => void | - | Handler called when the item is activated. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria MenuItem props.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | - | Content displayed in the list box item. |
| id | Key | - | Unique key for the item. |
| textValue | string | - | Text used for typeahead and accessibility. |
| isDisabled | boolean | - | Whether the item is disabled. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria ListBoxItem props.
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | - | Heading displayed above the section. |
| children | ReactNode | - | Menu items within the section. |
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria MenuSection props.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS class name for custom styling. |
Inherits all React Aria Separator props.
Submenus open to the right of their parent item.
<MenuTrigger>
<Button variant="secondary">Menu</Button>
<Menu>
<MenuItem>New File</MenuItem>
<SubmenuTrigger>
<MenuItem>Open Recent</MenuItem>
<Menu placement="right top">
<MenuItem>File 1.txt</MenuItem>
<MenuItem>File 2.txt</MenuItem>
</Menu>
</SubmenuTrigger>
<MenuItem>Save</MenuItem>
</Menu>
</MenuTrigger><MenuTrigger>
<Button variant="secondary">Menu</Button>
<Menu>
<MenuItem iconStart={<RiFileLine />}>New File</MenuItem>
<MenuItem iconStart={<RiFolderLine />}>New Folder</MenuItem>
<MenuItem iconStart={<RiImageLine />}>New Image</MenuItem>
</Menu>
</MenuTrigger>The href prop works with both internal and external links.
<MenuTrigger>
<Button variant="secondary">Menu</Button>
<Menu>
<MenuItem href="/home">Home</MenuItem>
<MenuItem href="/about">About</MenuItem>
<MenuItem href="/contact">Contact</MenuItem>
</Menu>
</MenuTrigger><MenuTrigger>
<Button variant="secondary">Menu</Button>
<Menu>
<MenuSection title="File">
<MenuItem>New</MenuItem>
<MenuItem>Open</MenuItem>
</MenuSection>
<MenuSection title="Edit">
<MenuItem>Cut</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Paste</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger><MenuTrigger>
<Button variant="secondary">Menu</Button>
<Menu>
<MenuItem>New</MenuItem>
<MenuItem>Open</MenuItem>
<MenuSeparator />
<MenuItem>Save</MenuItem>
<MenuItem>Save As...</MenuItem>
</Menu>
</MenuTrigger><MenuTrigger>
<Button variant="secondary">Search</Button>
<MenuAutocomplete placeholder="Type to search...">
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
<MenuItem>Option 3</MenuItem>
</MenuAutocomplete>
</MenuTrigger><MenuTrigger>
<Button variant="secondary">Select</Button>
<MenuAutocompleteListbox placeholder="Type to filter...">
<MenuListBoxItem>Option 1</MenuListBoxItem>
<MenuListBoxItem>Option 2</MenuListBoxItem>
<MenuListBoxItem>Option 3</MenuListBoxItem>
</MenuAutocompleteListbox>
</MenuTrigger>Set selectionMode="multiple" to allow multiple selections.
<MenuTrigger>
<Button variant="secondary">Multi-select</Button>
<MenuAutocompleteListbox
placeholder="Type to filter..."
selectionMode="multiple"
>
<MenuListBoxItem>Option 1</MenuListBoxItem>
<MenuListBoxItem>Option 2</MenuListBoxItem>
<MenuListBoxItem>Option 3</MenuListBoxItem>
</MenuAutocompleteListbox>
</MenuTrigger>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-MenuPopoverbui-MenuInnerbui-MenuContentMenu Breaking Simplified the neutral background prop API for container components. The explicit neutral-1, neutral-2, neutral-3, and neutral-auto values have been removed from ProviderBg. They are replaced by a single 'neutral' value that always auto-increments from the parent context, making it impossible to skip or pin to an explicit neutral level. #33002
Migration Guide:
Replace any explicit bg="neutral-1", bg="neutral-2", bg="neutral-3", or bg="neutral-auto" props with bg="neutral". To achieve a specific neutral level in stories or tests, use nested containers — each additional bg="neutral" wrapper increments by one level.
// Before
<Box bg="neutral-2">...</Box>
// After
<Box bg="neutral">
<Box bg="neutral">...</Box>
</Box>
Menu Breaking Removed --bui-bg-popover CSS token. Popover, Tooltip, Menu, and Dialog now use --bui-bg-app for their outer shell and Box bg="neutral-1" for content areas, providing better theme consistency and eliminating a redundant token. #32979
Migration Guide:
Replace any usage of --bui-bg-popover with --bui-bg-neutral-1 (for content surfaces) or --bui-bg-app (for outer shells):
- background: var(--bui-bg-popover);
+ background: var(--bui-bg-neutral-1);
Menu Breaking Centralized client-side routing in BUIProvider. Components like Link, ButtonLink, Tabs, Menu, TagGroup, and Table now require a BUIProvider rendered inside a React Router context for client-side navigation to work. #33267
Migration Guide:
This change requires updating @backstage/plugin-app and @backstage/core-app-api alongside @backstage/ui. If you only upgrade @backstage/ui, BUI components will fall back to full-page navigation.
If you cannot upgrade all packages together, or if you have a custom app shell, add a BUIProvider inside your Router:
+ import { BUIProvider } from '@backstage/ui';
<BrowserRouter>
+ <BUIProvider>
<AppContent />
+ </BUIProvider>
</BrowserRouter>
Menu Item Added analytics capabilities to the component library. Components with navigation behavior (Link, ButtonLink, Tab, MenuItem, Tag, Row) now fire analytics events on click when a BUIProvider is present.
New exports: BUIProvider, useAnalytics, getNodeText, and associated types (AnalyticsTracker, UseAnalyticsFn, BUIProviderProps, AnalyticsEventAttributes).
Components with analytics support now accept a noTrack prop to suppress event firing. #33150
Menu Migrated all components from useStyles to useDefinition hook. Exported OwnProps types for each component, enabling better type composition for consumers. #33050
Menu Fixed focus ring styles to use React Aria's [data-focus-visible] data attribute instead of the native CSS :focus-visible pseudo-class. This ensures keyboard focus rings render reliably when focus is managed programmatically by React Aria (e.g. inside a GridList, Menu, or Select). #33358
Menu Fixed scroll overflow in Menu and Select popover content when constrained by viewport height. #33049
Menu Fixed focus-visible outline styles for Menu and Select components. #32983
Menu Menu Item Menu Autocomplete Fixed client-side navigation for container components by wrapping the container (not individual items) in RouterProvider. Components now conditionally provide routing context only when children have internal links, removing the Router context requirement when not needed. This also removes the need to wrap these components in MemoryRouter during tests when they are not using the href prop.
Additionally, when multiple tabs match the current URL via prefix matching, the tab with the most specific path (highest segment count) is now selected. For example, with URL /catalog/users/john, a tab with path /catalog/users is now selected over a tab with path /catalog. #32373
Menu Breaking Changed className prop behavior to augment default styles instead of being ignored or overriding them.
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