build69
build69  /  Components  /  Buttons & Actions  /  SplitButton

SplitButton

v1.0.7Updated 27 Aug 2026

Primary action button with an attached caret segment that opens a dropdown of secondary actions. Both segments share Button's tone × emphasis × size API with correctly joined border radii, and the menu is fully keyboard

Preview

Live · 12 stories
DefaultOpen ↗
Why this is recommendedbuttondropdownmenuactions+4

The default, out-of-the-box SplitButton.

Recommended for+14
  • Save with alternate save modes (save & send, save as draft)
  • Export with format choices (PDF / print / copy link)
  • Record payment with method variants (cash / PromptPay / transfer)
Tags+6
buttondropdownmenuactionsprimary-actioncaretradixdefault
Features+4
  • Tone × emphasis × size shared across both segments (full recipe matrix)
  • Declarative items: label / icon / onSelect / destructive / disabled / separatorBefore
  • Loading state on the primary segment (caret disabled while loading)
  • Caret exposes aria-label ("More actions"), aria-haspopup and aria-expanded
  • Controlled and uncontrolled menu open state
  • Menu side/align control and full Radix keyboard navigation
  • prefers-reduced-motion respected for caret rotation and menu animation
Not recommended for−2
  • Unrelated action collections (use DropdownMenu alone)
  • Two equally weighted actions (use ButtonGroup)

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "SplitButton" })

Usage

import { SplitButton } from '@/components/actions/split-button';
import { Mail, Trash2 } from 'lucide-react';

<SplitButton
  onClick={saveInvoice}
  items={[
    { label: 'Save & send email', icon: <Mail />, onSelect: saveAndSend },
    { label: 'Delete draft', icon: <Trash2 />, destructive: true, separatorBefore: true, onSelect: removeDraft },
  ]}
>
  Save invoice
</SplitButton>

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
items

Secondary actions shown in the caret dropdown

SplitButtonItem[]yes
tone

Semantic tone shared by both segments

'neutral' | 'primary' | 'success' | 'warning' | 'danger' | 'info'
emphasis

Visual emphasis shared by both segments

'solid' | 'soft' | 'outline' | 'ghost' | 'link'
size

Size shared by both segments

'xs' | 'sm' | 'md' | 'lg'
loading

Spinner on the primary segment; caret disabled too

boolean
caretLabel

Accessible label for the caret trigger (default "More actions")

string
open

Controlled open state of the dropdown menu

boolean
onOpenChange

Open-state change callback

(open: boolean) => void

Every tonal component shares the same tone × emphasis API, so the pair looks identical across Button, Badge, Alert and more.

Related