Skip to main content

SplitButton

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

github
View source code
import { SplitButton } from '@uhg-abyss/web/ui/SplitButton';

Overview​

The SplitButton component is a button that has a main action and a dropdown menu. It is used to display a set of actions or functions that can be triggered by the user. The component is designed to be flexible and customizable, allowing you to define the menu items, their actions, and how they are displayed.

The dropdown, known as the Action Menu, can contain single menu items, checkboxes, radio groups, and submenus. Each item can have an icon.

Label​

Use the label prop to set the text of the main action button.

Outline​

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

Color contrast

For accessibility, we strongly recommend using outline if the SplitButton is placed on a background that does not meet the 3:1 color contrast ratio.

isDisabled​

Use the isDisabled prop to disable the entire SplitButton

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 two types of icons that can be added to the SplitButton:

  • Use the before prop to insert an icon before the main action button label.
  • 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​

There are two types of onClick functions you can use with the SplitButton:

  • To provide an action to perform when clicking the main action button, use the onClick prop of the SplitButton component.
  • To provide an action to perform when clicking a menu item, use the onClick prop of the menu item object.

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.

SplitButton Props​

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-action-menu-root" | "abyss-split-button-root" | "abyss-action-menu-portal" | "abyss-split-button-content" | "abyss-split-button-menu-items" | "abyss-split-button-main-action-button" | "abyss-split-button-menu-trigger", 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.
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 | number | bigint | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> | null | undefined
The label for the dropdown menu trigger; the visible text that also forms its accessible name for screen readers.
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.
onClick
((event: MouseClickEvent<HTMLElement>) => void) | undefined
Callback fired when the main action button is clicked.
This is the action that occurs when the user clicks the main part of the split button.
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
SplitButtonSlotPropsMap | undefined
Props to pass to individual internal slots of the component. To target
slots inside sub-components (e.g. the main action button, the menu
trigger, or the menu content), nest the sub-component's slotProps under
the parent slot key:
slotProps={{ 'split-button-main-action-button': { 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.

SplitButton Classes​

Class NameDescription
.abyss-action-menu-portalPortal element for the Action Menu within Split Button
.abyss-action-menu-rootRoot element for the Action Menu within Split Button
.abyss-split-button-contentMenu content container rendered when the dropdown is open
.abyss-split-button-main-action-buttonMain action button rendered on the left of the split button
.abyss-split-button-menu-itemsMenu items rendered inside the dropdown — one ActionMenuItem/CheckboxItem/RadioItem/SubMenuTrigger/Separator per `menuItems` entry
.abyss-split-button-menu-triggerMenu trigger button rendered on the right of the split button
.abyss-split-button-rootContainer element for Split Button

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, SplitButton 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.

SplitButton Tokens​

Token NameValue
split-button.color.border.outline-button.default
#002677
split-button.color.border.outline-button.disabled
#CBCCCD
split-button.color.border.text-button.rest
#196ECF
split-button.color.border.text-button.hover
#004BA0
split-button.color.border.text-button.active
#002677
split-button.color.border.text-button.disabled
#7D7F81
split-button.color.icon.outline-button.default
#002677
split-button.color.icon.outline-button.disabled
#4B4D4F
split-button.color.icon.text-button.rest
#196ECF
split-button.color.icon.text-button.hover
#004BA0
split-button.color.icon.text-button.active
#002677
split-button.color.icon.text-button.disabled
#7D7F81
split-button.color.icon.text-button.selected
#196ECF
split-button.color.surface.container.rest
#FFFFFF
split-button.color.surface.container.hover
#F3F3F3
split-button.color.surface.container.active
#E5E5E6
split-button.color.surface.container.disabled
#F3F3F3
split-button.color.text.label.outline-button.default
#002677
split-button.color.text.label.outline-button.disabled
#4B4D4F
split-button.color.text.label.text-button.rest
#196ECF
split-button.color.text.label.text-button.hover
#004BA0
split-button.color.text.label.text-button.active
#002677
split-button.color.text.label.text-button.disabled
#7D7F81
split-button.border-radius.all.container
500px
split-button.border-width.all.container
1px
split-button.sizing.all.icon.utility
20px
split-button.spacing.gap.horizontal.button
8px
split-button.spacing.gap.vertical.button-dropdown
8px
split-button.spacing.padding.left.button
16px
split-button.spacing.padding.right.label
8px
split-button.spacing.padding.right.menu-trigger
8px
split-button.spacing.padding.vertical.menu-trigger
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