SplitButton
A primary action with a menu of related actions attached. Button plus DropdownMenu.
Usage
import { SplitButton } from "@azmr/ui/split-button";
import { DropdownMenuItem } from "@azmr/ui/menu";Nothing saved yet
"use client";
import { DropdownMenuItem, DropdownMenuSeparator } from "@azmr/ui/menu";
import { SplitButton } from "@azmr/ui/split-button";
import { useState } from "react";
export default function SplitButtonBasic() {
const [last, setLast] = useState("Nothing saved yet");
return (
<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
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.