SplitButton
A primary action with a menu of related actions attached. Button plus DropdownMenu.
View as Markdown for coding assistants, or see llms.txt.
- Stable
- Group: Inputs
- Added in @azmr/ui 0.2.0
Usage
import { SplitButton } from "@azmr/ui/split-button";
import { DropdownMenuItem } from "@azmr/ui/menu";Nothing saved yet
<div className="flex items-center gap-4">
<SplitButton
menuLabel="More save options"
onClick={() => setLast("Saved")}
menu={
<>
<DropdownMenuItem onClick={() => setLast("Saved as a new file")}>
Save as…
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setLast("Saved a copy")}>Save a copy</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setLast("Saved and closed")}>
Save and close
</DropdownMenuItem>
</>
}
>
Save
</SplitButton>
<SplitButton
variant="outline"
menuLabel="More export options"
menu={<DropdownMenuItem>PDF</DropdownMenuItem>}
>
Export
</SplitButton>
<p className="text-sm text-muted-foreground" aria-live="polite">
{last}
</p>
</div><SplitButton
menuLabel="More save options"
onClick={save}
menu={
<>
<DropdownMenuItem onClick={saveAs}>Save as…</DropdownMenuItem>
<DropdownMenuItem onClick={saveCopy}>Save a copy</DropdownMenuItem>
</>
}
>
Save
</SplitButton>The main button runs the common action. The arrow opens a menu of alternatives, so the common case stays one click. menu takes any DropdownMenu parts: items, separators, groups, even checkbox items.
menuLabel is required. The arrow button has no text, so it needs a name ("More save options"), and the types enforce it. The arrow also announces that it opens a menu (aria-haspopup, aria-expanded).
Props
Prop
Type
variant and size apply to both halves, so the pair stays level; filled variants get a divider between them and outline shares one border.
loadingshows a spinner on the main button and ignores it, while the menu stays usable: a slow "Save" should not stop someone choosing "Save as…".loadingTextandstartIconwork as onButton.disableddisables both halves.
Everything else a Button accepts (onClick, type, aria-*) goes to the main button.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves to the main button, then to the arrow button. |
| EnterorSpace | On the main button: runs the primary action. On the arrow: opens the menu. |
| ArrowDown | On the arrow button: opens the menu on its first item. |
| Escape | Closes the menu and returns focus to the arrow button. |
Once the menu is open it follows the DropdownMenu keys.
Size
The menu is loaded with the split button, so this entry carries the menu's cost (about the same as @azmr/ui/menu). If a page only needs a plain Button, import that instead.
Accessibility
- Two real buttons: the primary action, and the arrow that opens the menu. Tab visits both, in that order.
- The arrow has no text, so
menuLabelis required and the types enforce it. It becomes the arrow's accessible name ("More save options"). - The arrow carries
aria-haspopup="menu"andaria-expanded, which changes fromfalsetotruewhile the menu is open. disableddisables both halves, andloadingleaves the menu usable. The menu follows the DropdownMenu semantics.- The component's tests run axe with the menu closed and open.