Actions
Split Button
A default-action button paired with a menu of alternatives.
When to use it: Use it when one action is the common choice but a few related ones should stay one reach away.
Example
import {
Button,
Menu,
MenuItem,
MenuList,
MenuPopover,
MenuTrigger,
SplitButton,
} from "@comp0/react";
import { ChevronDownIcon } from "@heroicons/react/16/solid";
export function Example() {
const save = () => {};
const saveAs = () => {};
const saveCopy = () => {};
return (
<SplitButton aria-label="Save" className="inline-flex items-stretch rounded-md shadow-sm">
<Button
onClick={save}
className="rounded-l-md bg-teal-600 px-3 py-2.5 text-base text-white data-hovered:bg-teal-700 data-focus-visible:outline data-focus-visible:-outline-offset-2 data-focus-visible:outline-2 data-focus-visible:outline-white sm:py-2 sm:text-sm dark:bg-teal-500 dark:text-teal-950 dark:data-hovered:bg-teal-400"
>
Save
</Button>
<Menu>
<MenuTrigger
aria-label="More save options"
className="flex items-center rounded-r-md border-l border-white/30 bg-teal-600 px-2 text-white hover:bg-teal-700 focus-visible:outline focus-visible:-outline-offset-2 focus-visible:outline-2 focus-visible:outline-white dark:border-teal-950/20 dark:bg-teal-500 dark:text-teal-950 dark:hover:bg-teal-400"
>
<ChevronDownIcon className="size-4" aria-hidden="true" />
</MenuTrigger>
<MenuPopover
placement="bottom end"
offset={8}
className="w-44 rounded border-0 bg-white p-1 shadow-lg ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:shadow-none dark:ring-white/10"
>
<MenuList>
<MenuItem
onClick={saveAs}
value="save-as"
className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
>
Save as…
</MenuItem>
<MenuItem
onClick={saveCopy}
value="save-copy"
className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
>
Save a copy…
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>
</SplitButton>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
SplitButton
Groups the default button and the menu button as one tab stop; arrow keys move between them. Owns a DOM element.
Button
The default action; disabling it drops it from the arrow-key order. Owns a DOM element.
MenuTrigger
The menu button that opens the alternatives. Owns a DOM element.
MenuPopover / MenuList / MenuItem
The floating surface, action list, and alternative actions from the Menu family. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap a Button and a Menu in SplitButton and give it an aria-label.
- 2
Add the supporting parts
Put the default action on the Button; give the MenuTrigger its own name such as More options.
- 3
Make the behavior clear
Arrow keys move between the two segments as one tab stop; the menu opens from its own button.
Exampletsx <SplitButton aria-label="Save"><Button onClick={save}>Save</Button><Menu><MenuTrigger aria-label="More save options">▾</MenuTrigger><MenuPopover><MenuList><MenuItem onClick={saveAs}>Save as…</MenuItem></MenuList></MenuPopover></Menu></SplitButton>
Keyboard
- ⇥
- Moves into the control at the last-used segment; Tab again leaves.
- →
- Moves to the next segment without wrapping.
- ←
- Moves to the previous segment without wrapping.
- Home
- Moves to the first segment.
- End
- Moves to the last segment.
- ↓
- Opens the menu from the menu button. · menu button
- ↵
- Presses the focused segment.
- Space
- Presses the focused segment.
Forms and accessibility
A split button does not create form values; its buttons submit their own.
Accessibility checklist
- Give SplitButton an aria-label and the menu button its own distinct name.
- Keep the default action first so it reads and roves before the menu button.
- Offer the menu's actions somewhere else too; a hidden menu is easy to miss.
API reference
import { Button, Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, SplitButton } from "@comp0/react";SplitButton
DOM elementGroups the default button and the menu button as one tab stop; arrow keys move between them.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the whole control for assistive technology. |
Button
DOM elementThe default action; disabling it drops it from the arrow-key order.
| Prop | Type | Description |
|---|---|---|
onClick | (event) => void | Runs the default action. |
disabled | boolean | Removes this segment from the tab stop. |
MenuTrigger
DOM elementThe menu button that opens the alternatives.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the menu segment, distinct from the default action. |
MenuPopover / MenuList / MenuItem
DOM elementThe floating surface, action list, and alternative actions from the Menu family.