Skip to main content

DropdownMenu

Displays a menu triggered by a button, such as a set of actions or functions.

github
View source code

Migration Information

For teams migrating from the V1 to V2 component, please refer to the migration guide for changes to the component.
Component Guide
import { DropdownMenu } from '@uhg-abyss/web/ui/DropdownMenu';

Overview​

The DropdownMenu component displays a menu triggered by a button, known as the Action Menu. This menu can contain various items, including action items, checkboxes, radio groups, and submenus. Each item can have an icon.

Label​

Use the label prop to set the text displayed on the trigger. Alternatively, you can use the iconOnly prop to display only an icon without text.

Always use a label

To meet accessibility standards, you must still provide a label prop when using iconOnly.

Outline​

Use the outline prop to control the outline of the Dropdown menu. The default is true. When setting the prop to false, the border radius will be removed and the component will have a flat appearance.

Important

An outline should be used if the DropdownMenu is used on a background that does not meet the 3:1 color contrast ratio.

isDisabled​

Use the isDisabled prop to disable the DropdownMenu.

isLoading​

Use the isLoading prop to place the Action Menu in a loading state. This will display a loading spinner in place of the menu items while the menu is loading.

You can also provide the optional prop loadingLabel for additional context when the menu is in a loading state. This prop takes the form of an object, as explained in our LoadingSpinner documentation.

Use the menuItems prop to specify what will be displayed in the Action Menu. The prop requires an array of objects that have the following forms:

Action item​

interface ActionMenuItemProps {
title: string;
onClick: Function;
icon: ReactNode; //optional
isSeparated: boolean; //optional
disabled: boolean; //optional
}

Checkbox​

interface ActionMenuCheckboxItemProps {
checkboxes: Array<{
label: string;
checked: bool;
onChange: func;
disabled: bool; //optional
}>;
}

Radio group​

interface ActionMenuRadioItemProps {
label: string;
value: string;
onChange: Function;
radios: Array<{
label: string;
value: string;
disabled: bool; //optional
}>;
}
interface ActionMenuItemGroupSubMenuItem {
title: string;
subMenu: ActionMenuItemGroupItem[]; // any of the above types
}

Inserting icons​

There are three different types of icons that can be added to the DropdownMenu:

  • Use the before prop to insert an icon before the trigger label.
  • Use the iconOnly prop to only display an icon in the trigger button without displaying the label.
  • The iconOnly prop can be used to display only an icon in the trigger button without displaying the label.
    • iconOnly also accepts a React node, allowing you to use any custom icon component.
  • Use the icon prop in the menu items to insert icons before to the item title. While this prop accepts any React node, the examples on this page use our IconSymbol component.

onClick​

Use the onClick function on each menu item to trigger a custom function when that item is clicked.

onChange​

Use the onChange function to trigger a custom function when a checkbox or a radio item is clicked. You can use this to update your checked state, among other things.

Disabled menu items​

When the disabled flag is set to true on a menu item, that item cannot be interacted with. This prop applies to all item types, including action items, checkboxes, and radio groups. The item will be visually styled to indicate that it is disabled.

isSeparated​

When the isSeparated flag is set to true on a menu item, a horizontal divider will be inserted after that item. Checkbox and radio group items automatically render a divider both before and after the item, so no isSeparated flag is required. A divider will not be rendered before the first item or after the last item.

onOutsideClick​

Use onOutsideClick prop to trigger a custom function when the user clicks outside an open menu.

Use the open and onOpenChange props together to control the dropdown open state. open is a boolean that determines whether the dropdown is open or closed, and onOpenChange is a function that is called when the open state changes. This allows you to manage the open state of the dropdown programmatically.

Height​

Use the height prop to set a custom height for the Action Menu. The default height is 500px. This prop accepts any valid CSS height value.

SplitButton vs. DropdownMenu​

DropdownMenu is a versatile component that can be used for various menu actions, while SplitButton is specifically designed for a button with a dropdown menu that allows users to perform an action or select from a list of options.SplitButton is more suitable when you want to combine a primary action with a secondary dropdown menu.

Name
Type
DefaultRequiredA11yDescription
before
string | number | bigint | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> | null | undefined
* A React node (IconSymbol) to be displayed before the label in the dropdown trigger.
className
string | undefined
CSS class name to apply to each element within the component
css
CSSProperties | Partial<Record<"abyss-dropdown-menu-portal" | "abyss-dropdown-menu-root" | "abyss-dropdown-menu-content" | "abyss-dropdown-menu-trigger" | "abyss-dropdown-menu-menu-items", CSSProperties>> | undefined
Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation
See CSS Prop Styling for more information
data-testid
string | undefined
Suffix used to create a unique ID used for automated testing
See Component Testing for more information
disableDefaultProviderProps
boolean | undefined
false
If set to true, the component will not use the DefaultPropsProvider values.

If you aren’t using the DefaultPropsProvider, this prop can be ignored.
height
Height<string | number> | undefined
500
The height of the menu content area.
hiddenFrom
Breakpoint | undefined
When defined, the component is hidden once the screen width reaches the specified breakpoint.
iconOnly
string | number | bigint | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> | null | undefined
false
If set to true, renders a standard icon button. If set to false, renders a button with a label.
Alternatively, you can provide an IconSymbol to use a custom icon element.
isDisabled
boolean | undefined
false
Disables the dropdown menu trigger and interactions.
isLoading
boolean | undefined
false
Displays a loading state for the dropdown menu content.
label
string
The label for the dropdown menu trigger; used as its accessible name even when iconOnly is true.
loadingLabel
{ heading: string; headingLevel?: 1 | 2 | 3 | 4 | 5 | 6 | undefined; bodyText?: string | undefined; } | undefined
The label shown when the dropdown content is loading.
menuItems
GenericActionMenuItem[] | undefined
An array of menu items to be rendered in the dropdown.
modal
boolean | undefined
false
Determines if the dropdown behaves as a modal dialog.
Note: automatically set to false in shadow DOM contexts.
onOpenChange
((open: boolean) => void) | undefined
Callback fired when the dropdown menu open state changes.
onOutsideClick
((event: MouseEvent | TouchEvent) => void) | undefined
Callback fired when a click outside the dropdown menu is detected.
open
boolean | null | undefined
null
Controls the open state of the dropdown menu (controlled mode).
outline
boolean | undefined
true
Applies an outline style to the dropdown menu trigger.
slotProps
DropdownMenuSlotPropsMap | undefined
Props to pass to individual internal slots of the component. To target
slots inside sub-components (e.g. the trigger or menu content), nest the
sub-component's slotProps under the parent slot key:
slotProps={{ 'dropdown-menu-trigger': { slotProps: { '<inner-key>': {...} } } }}
See slotProps for more information
visibleFrom
Breakpoint | undefined
When defined, the component is hidden until the screen width reaches the specified breakpoint.

ActionMenu - Menu Item Props​

Name
Type
DefaultRequiredA11yDescription
className
string | undefined
CSS class name to apply to each element within the component
css
CSSProperties | Partial<Record<"abyss-action-menu-item-list-item" | "abyss-action-menu-item-left-container" | "abyss-action-menu-item-title" | "abyss-action-menu-item", CSSProperties>> | undefined
Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation
See CSS Prop Styling for more information
data-testid
string | undefined
Suffix used to create a unique ID used for automated testing
See Component Testing for more information
disabled
boolean | undefined
false
If true, the menu item will be disabled
disableDefaultProviderProps
boolean | undefined
false
If set to true, the component will not use the DefaultPropsProvider values.

If you aren’t using the DefaultPropsProvider, this prop can be ignored.
hiddenFrom
Breakpoint | undefined
When defined, the component is hidden once the screen width reaches the specified breakpoint.
icon
string | number | bigint | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> | null | undefined
''
The icon type to display in the menu item. Decorative icons should be left without a text alternative; if the icon conveys meaning, provide a brief description.
isSeparated
boolean | undefined
false
If true, the menu item will be separated by a horizontal line
onClick
((event: MouseClickEvent<HTMLElement>) => void) | undefined
Callback function when the menu item is clicked
slotProps
Partial<Record<string, SlotPropsValue<string>>> | undefined
Props to pass to individual internal slots of the component, keyed by the slot name
(e.g. 'button-root'). Supports className, css, style, aria-*, data-*,
data-testid, and nested slotProps for child Abyss components.
See slotProps for more information
title
string
The title of the menu item; used as its accessible name for screen readers.
visibleFrom
Breakpoint | undefined
When defined, the component is hidden until the screen width reaches the specified breakpoint.

ActionMenu - Checkbox Item Props​

Name
Type
DefaultRequiredDescription
checkboxes
CheckboxOption[]
An array of checkbox options to display.
Each checkbox option should include a label and optionally disabled, checked state, and change handler.
className
string | undefined
CSS class name to apply to each element within the component
css
CSSProperties | Partial<Record<"abyss-action-menu-checkbox-list-item" | "abyss-action-menu-checkbox-icon" | "abyss-action-menu-checkbox", CSSProperties>> | undefined
Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation
See CSS Prop Styling for more information
data-testid
string | undefined
Suffix used to create a unique ID used for automated testing
See Component Testing for more information
disableDefaultProviderProps
boolean | undefined
false
If set to true, the component will not use the DefaultPropsProvider values.

If you aren’t using the DefaultPropsProvider, this prop can be ignored.
hiddenFrom
Breakpoint | undefined
When defined, the component is hidden once the screen width reaches the specified breakpoint.
isSeparatedBottom
boolean | undefined
false
If true, a separator is shown below the last checkbox.
isSeparatedTop
boolean | undefined
false
If true, a separator is shown above the first checkbox.
slotProps
Partial<Record<string, SlotPropsValue<string>>> | undefined
Props to pass to individual internal slots of the component, keyed by the slot name
(e.g. 'button-root'). Supports className, css, style, aria-*, data-*,
data-testid, and nested slotProps for child Abyss components.
See slotProps for more information
visibleFrom
Breakpoint | undefined
When defined, the component is hidden until the screen width reaches the specified breakpoint.

ActionMenu - Radio Item Props​

Name
Type
DefaultRequiredA11yDescription
className
string | undefined
CSS class name to apply to each element within the component
css
CSSProperties | Partial<Record<"abyss-action-menu-radio-group-list-item" | "abyss-action-menu-radio-group" | "abyss-action-menu-radio-group-label" | "abyss-action-menu-radio-list" | "abyss-action-menu-radio-list-item" | "abyss-action-menu-radio" | "abyss-action-menu-radio-icon" | "abyss-action-menu-radio-title", CSS...
Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation
See CSS Prop Styling for more information
data-testid
string | undefined
Suffix used to create a unique ID used for automated testing
See Component Testing for more information
disabled
boolean | undefined
false
If true, disables the entire radio group.
disableDefaultProviderProps
boolean | undefined
false
If set to true, the component will not use the DefaultPropsProvider values.

If you aren’t using the DefaultPropsProvider, this prop can be ignored.
hiddenFrom
Breakpoint | undefined
When defined, the component is hidden once the screen width reaches the specified breakpoint.
isSeparatedBottom
boolean | undefined
true
If true, a separator is shown below the radio group.
isSeparatedTop
boolean | undefined
true
If true, a separator is shown above the radio group.
label
string
''
The label for the radio group; used as its accessible name via aria-labelledby.
onChange
((value: any) => void) | undefined
Callback when the selected radio changes.
radios
RadioOption[]
An array of radio items to display.
Each radio item should include a label, value, and optionally disabled state.
slotProps
Partial<Record<string, SlotPropsValue<string>>> | undefined
Props to pass to individual internal slots of the component, keyed by the slot name
(e.g. 'button-root'). Supports className, css, style, aria-*, data-*,
data-testid, and nested slotProps for child Abyss components.
See slotProps for more information
value
any
The current value for the radio group.
visibleFrom
Breakpoint | undefined
When defined, the component is hidden until the screen width reaches the specified breakpoint.

ActionMenu - Item Group Props​

Name
Type
Description
firstItemRef
Ref<HTMLElement> | undefined
The ref for the first item in the group. Typically used for focus control.
menuItems
GenericActionMenuItem[] | undefined
An array of menu items. Each item can be a basic menu item,
a submenu trigger, a checkbox group, or a radio group.
width
string | number | undefined
The width that should be applied to the rendered menu group.

ActionMenu - SubMenu Trigger Props​

Name
Type
DefaultRequiredA11yDescription
className
string | undefined
CSS class name to apply to each element within the component
css
CSSProperties | Partial<Record<"abyss-action-menu-item-left-container" | "abyss-action-menu-item-title" | "abyss-action-menu-sub-trigger" | "abyss-action-menu-sub-trigger-right-container" | "abyss-action-menu-sub-trigger-icon-right", CSSProperties>> | undefined
Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation
See CSS Prop Styling for more information
data-testid
string | undefined
Suffix used to create a unique ID used for automated testing
See Component Testing for more information
disabled
boolean | undefined
false
Whether the sub menu trigger is disabled.
disableDefaultProviderProps
boolean | undefined
false
If set to true, the component will not use the DefaultPropsProvider values.

If you aren’t using the DefaultPropsProvider, this prop can be ignored.
hiddenFrom
Breakpoint | undefined
When defined, the component is hidden once the screen width reaches the specified breakpoint.
icon
string | number | bigint | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> | null | undefined
''
The icon type to display in the menu item
isOpen
boolean | undefined
false
Whether the sub menu trigger is open.
slotProps
Partial<Record<string, SlotPropsValue<string>>> | undefined
Props to pass to individual internal slots of the component, keyed by the slot name
(e.g. 'button-root'). Supports className, css, style, aria-*, data-*,
data-testid, and nested slotProps for child Abyss components.
See slotProps for more information
title
string
Title of the sub menu trigger item; used as its accessible name.
visibleFrom
Breakpoint | undefined
When defined, the component is hidden until the screen width reaches the specified breakpoint.

Class NameDescription
.abyss-dropdown-menu-contentMenu content container rendered when the dropdown is open
.abyss-dropdown-menu-menu-itemsMenu items rendered inside the dropdown — one ActionMenuItem/CheckboxItem/RadioItem/SubMenuTrigger/Separator per `menuItems` entry
.abyss-dropdown-menu-portalPortal container for the dropdown menu
.abyss-dropdown-menu-rootRoot container for the dropdown menu
.abyss-dropdown-menu-triggerMenu trigger button that opens the dropdown

Use for application menu settings and actions only - NOT navigation​

From an accessibility and WAI-ARIA perspective, dropdown and split button menus are intended only for application settings and actions—not navigation. Unlike navigation components, these rely solely on arrow keys for making selections. When using navigation components, tabbing between links is an expected behavior. Use NavMenu or other components for navigating between web pages.

WAI-ARIA implementation​

This component implements several WAI-ARIA design patterns beginning with Menu Buttonopens in a new window. Nested menus are of single-menu examples from Menu Baropens in a new window (using roving tabindexopens in a new window to manage focus movement). Radio button and checkbox option operation are based upon the Editor Menu Exampleopens in a new window.

By default, DropdownMenu uses non-modal behavior (modal={false}), so opening the menu does not apply aria-hidden to surrounding page landmarks.

Checkbox and radio indicators inside menu items use disabled native inputs with a silent aria-label="," so automated accessibility checks can validate labeling and nested-interactive behavior without changing keyboard interaction on the menu item itself.

Known Axe false positives​

When menus are displayed, Axe may report a region (moderate) issue because menu portal content may not be nested in <main> or another landmark. This is expected for this pattern because menu behavior is JavaScript-controlled and modal-like while open.

Known accessibility issues​

JAWS​

Radio button groups​

  • When there is only one group of radio buttons, the group name is only announced when first entered.
  • When there are multiple groups, the group name is consistently announced.

Keyboard Interactions​

KeyDescription
TabMoves focus to the next focusable element.
EnterIf focus is on a Dropdown menu button, opens the relevant Dropdown menu. If focus is on a menu item with a trigger, opens the relevant sub-menu. If focus is on radio button or checkbox, will select/deselect it.
SpaceIf focus is on a Dropdown menu button, opens the relevant Dropdown menu. If focus is on a menu item with a trigger, opens the relevant sub-menu. If focus is on radio button or checkbox, will select/deselect it.
EscapeCloses open Dropdown menu and moves focus to its Dropdown menu button.
Up ArrowFocuses the previous menu item.
Down ArrowFocuses the next menu item.
Right ArrowOpens a sub-menu if focus is on a menu item with a trigger.
Left ArrowCloses a sub-menu if open.
HomeMoves focus to the first item in the Dropdown menu.
EndMoves focus to the last item in the Dropdown menu.

Component Tokens​

Tip

Click on the token row to copy the token to your clipboard.

Token NameValue
dropdown-menu.color.border.button
#002677
dropdown-menu.color.icon.secondary.default
#002677
dropdown-menu.color.icon.secondary.disabled
#4B4D4F
dropdown-menu.color.icon.tertiary.rest
#196ECF
dropdown-menu.color.icon.tertiary.hover
#004BA0
dropdown-menu.color.icon.tertiary.active
#002677
dropdown-menu.color.icon.tertiary.disabled
#7D7F81
dropdown-menu.color.icon.tertiary.selected
#196ECF
dropdown-menu.color.surface.container.rest
#FFFFFF
dropdown-menu.color.surface.container.hover
#F3F3F3
dropdown-menu.color.surface.container.active
#E5E5E6
dropdown-menu.color.surface.container.disabled
#F3F3F3
dropdown-menu.color.surface.container.selected
#EDF3FB
dropdown-menu.color.text.label.secondary.default
#002677
dropdown-menu.color.text.label.secondary.disabled
#4B4D4F
dropdown-menu.color.text.label.tertiary.rest
#196ECF
dropdown-menu.color.text.label.tertiary.hover
#004BA0
dropdown-menu.color.text.label.tertiary.active
#002677
dropdown-menu.color.text.label.tertiary.disabled
#7D7F81
dropdown-menu.color.text.label.tertiary.selected
#196ECF
dropdown-menu.border-radius.all.container
500px
dropdown-menu.border-width.all.button
1px
dropdown-menu.sizing.all.icon.utility
20px
dropdown-menu.sizing.height.button
40px
dropdown-menu.sizing.width.button
40px
dropdown-menu.spacing.gap.horizontal.button
8px
dropdown-menu.spacing.gap.vertical.button-dropdown
8px
dropdown-menu.spacing.padding.all.icon-only
8px
dropdown-menu.spacing.padding.left.default
16px
dropdown-menu.spacing.padding.right.default
12px
dropdown-menu.spacing.padding.horizontal.leading-icon
12px
dropdown-menu.spacing.padding.vertical.button
8px

ActionMenu Tokens​

Token NameValue
action-menu.color.border.container
#CBCCCD
action-menu.color.surface.container
#FFFFFF
action-menu.color.surface.menu-item.rest
#FFFFFF
action-menu.color.surface.menu-item.hover
#F3F3F3
action-menu.color.surface.menu-item.active
#E5E5E6
action-menu.color.surface.separator
#CBCCCD
action-menu.color.text.menu-item.default
#4B4D4F
action-menu.color.text.menu-item.disabled
#7D7F81
action-menu.color.icon.menu-item.default
#323334
action-menu.color.icon.menu-item.disabled
#7D7F81
action-menu.border-radius.all.container
4px
action-menu.border-width.all.container
1px
action-menu.spacing.gap.horizontal.menu-item
8px
action-menu.spacing.margin.all.separator
8px
action-menu.spacing.padding.horizontal.menu-item
16px
action-menu.spacing.padding.vertical.loading
24px
action-menu.spacing.padding.vertical.menu-item
8px
Table of Contents