Actions
Split Button
Split Button joins one primary Button action with a related Menu trigger using shared outer chrome — composition only; Button and Menu keep their own APIs.
Style × Size — 9 variants
betaReact AvailableFigma AvailableDocs Partial
- React last updated
- 2026-09-14
- Documentation last updated
- 2026-09-14
- Accessibility target
- WCAG 2.2 AA (target)
- Version
- 0.1.0-beta
Known open questions
- Figma set has no State / Shape / Surface / menu-open variants — those remain Button + Menu responsibilities.
- Chevron trigger padding follows Button control padding (Figma chevron segment is slightly tighter horizontally).
- Disabled/loading combinations are independent per segment; no group-level loading API.
Live preview
Primary Button + MenuTrigger Button joined with Button Group chrome. Menu owns the popup; Split Button owns adjacency only.
Primary (divider=primary)
Last action: No action yet
Secondary (divider=neutral)
Last action: No action yet
Danger (divider=danger)
Last action: No action yet
Sizes
Last action: No action yet
Last action: No action yet
Last action: No action yet
Disabled primary
Last action: No action yet
Disabled menu
Last action: No action yet
Loading primary
Last action: No action yet
Purpose
Split Button joins one primary default action with an adjacent menu trigger for closely related alternatives (e.g. "Save" + a chevron revealing "Save as draft" / "Save and publish").
Anatomy
SplitButton = role=group wrapper (shared border + divider gap + outer radius) + primary Button + Menu (MenuTrigger Button + MenuContent). Divider tone is chrome only.
Variants and states
neutral · primary · danger
When to use
When there's one clear default action but a few related variants a user occasionally wants instead.
When not to use
When all options are equally likely — use Menu or Select. When actions are peer siblings without a default — use Button Group. Do not use for exclusive selection (Tabs / Toggle Group).
Accessibility
Two independently focusable, independently labeled controls (two real buttons) inside role="group". Primary Button has no menu ARIA. MenuTrigger Button owns aria-haspopup / aria-expanded. Natural Tab order; Escape closes via Menu.
Keyboard behavior
Tab between primary Button and MenuTrigger Button. Enter/Space activate the focused control. Menu owns ArrowDown/ArrowUp open, item navigation, and Escape close.
Common mistakes
Treating this as one button. Putting Button or Menu props on SplitButton. Opening the menu from the primary action. Replacing Menu with a custom dropdown. Confusing with Button Group.
Properties
Figma: Style × Size + Label (main action only). React: children (primary Button + Menu composition) + divider (neutral|primary|danger). Menu items are Menu’s API, not SplitButton props.
How is Split Button different from Button Group?
Button Group joins peer independent actions. Split Button pairs one primary default action with a related secondary menu trigger — not interchangeable APIs.
Does Split Button own menu items?
No. Compose Menu, MenuTrigger, MenuContent, and MenuItem. Split Button only provides joined chrome between the primary Button and the trigger Button.
Tokens used
component/radius/controlsemantic/border/defaultcolor/brand/700color/danger/700
Known limitation
Figma set has no State / Shape / Surface / menu-open axes — those stay on Button and Menu. Chevron segment padding follows Button (may be slightly wider than Figma’s tighter trigger). /r deferred to CE-3.
Component API
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Primary Skrewww Button plus a Menu whose MenuTrigger wraps a secondary Button (typically CaretDown with aria-label). Do not put Button/Menu props on SplitButton. |
| divider | "neutral" | "primary" | "danger" | "neutral" | Chrome color in the 2px gap — matches Figma Style gap fill (neutral=Secondary, primary, danger). Not a Button variant. |
| aria-label | string | — | Accessible name for the primary+menu pair when no visible group label exists. |
| aria-labelledby | string | — | ID of a visible label element for the pair. |
React example
Copy React example
import { CaretDown } from "@phosphor-icons/react";
import { Button } from "@/components/ui/Button";
import {
Menu,
MenuContent,
MenuItem,
MenuTrigger,
} from "@/components/ui/Menu";
import { SplitButton } from "@/components/ui/SplitButton";
export function Example() {
return (
<SplitButton aria-label="Save options" divider="primary">
<Button type="button" variant="primary" onClick={() => {}}>
Save
</Button>
<Menu>
<MenuTrigger>
<Button type="button" variant="primary" aria-label="More save options">
<CaretDown size={16} weight="bold" />
</Button>
</MenuTrigger>
<MenuContent aria-label="More save options">
<MenuItem onSelect={() => {}}>Save as draft</MenuItem>
<MenuItem onSelect={() => {}}>Save and publish</MenuItem>
</MenuContent>
</Menu>
</SplitButton>
);
}