skrewww

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.

Surface
Shape

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

PropTypeDefaultDescription
childrenReactNodePrimary 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-labelstringAccessible name for the primary+menu pair when no visible group label exists.
aria-labelledbystringID 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>
  );
}