Split button
One default action with related alternatives.
Usage
import { SplitButton } from '@commenda/pathfinder/split-button';
<SplitButton
label="Export entities"
onClick={onExport}
loadingConfig={{ isLoading: isExporting }}
menuConfig={{
label: 'More export options',
groups: [
{
id: 'formats',
items: [
{
id: 'csv',
type: 'action',
label: 'Export CSV',
onClick: onExportCsv,
},
{
id: 'pdf',
type: 'action',
label: 'Export PDF',
onClick: onExportPdf,
},
],
},
],
}}
/>;Installation
pnpm --filter your-app add "@commenda/pathfinder@workspace:*"The package currently lives in the Commenda workspace. External installation requires a published version or packed tarball. Import the precompiled stylesheet once; no application Tailwind setup is required.
Examples
Secondary
Loading the main action
API reference
| Prop | Type | Behavior |
|---|---|---|
className | string | Additional classes for the component root. |
size | "sm"|"md"|"lg" | — |
loadingConfig | LoadingConfigView fields | — |
leadingIcon | ReactNode | — |
disabledReason | string | — |
openConditional | boolean | — |
onOpenChangeConditional | (open: boolean) => void | — |
defaultOpen | boolean | — |
labelRequired | string | Visible field label. Supply an accessible name when it is omitted. |
variant | "primary"|"secondary" | — |
completionConfig | CompletionConfigView fields | — |
menuConfigRequired | SplitButtonMenuConfigView fields | — |
SplitButtonMenuConfig: required groups: DropdownGroup[]; optional label, disabled, disabledReason, side, align, sideOffset, portalContainer. Label defaults to More options for ${label}. Menu configuration disables only alternatives; component disabled disables both halves. There are no public Root/Trigger/Content parts or top-level menuLabel/menuDisabled props.
SPLIT-ACTION-01: Use one obvious default with two to five related alternatives. The main half acts immediately; the chevron opens the menu. Each is a separate keyboard stop with independent hover/focus. Loading blocks only the main action; disable the menu explicitly when alternatives conflict with ongoing work. Escape closes the menu and restores trigger focus. The menu trigger never submits a form.
No tertiary or destructive default. Destructive alternatives belong last in the menu and generally require confirmation. Without a default action use Dropdown with a normal Button. Do not put two SplitButtons beside each other in a row. Inner corners are square; outer corners follow Button radius. Recursive menus use the same model as Dropdown.
Import @commenda/pathfinder/styles.css once at the app root. Event handlers require a client component in Next.js App Router.
Design rules
Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.
Action rules
-
CONTROL-STATE-01— Pathfinder: loadingConfig and completionConfig are explicit application signals. Loading wins over completion; disabled blocks activation. Ending loading never implies success. Applications own the 1.5-second completion reset recipe from Figma. -
CONTROL-SIZE-01— Figma: Button and SplitButton sm/md/lg have 28/32/36px visual heights; md default. IconButton adds xs 24. Button icons are 16 through md and 20 at lg; pass size to generated icons to select optical artwork. Link styles have text-sized geometry. Long content can increase minimum height for accessibility. -
CONTROL-FOCUS-01— Figma: show focus from keyboard, not pointer click. Button family uses a blue ring separated by a white gap. Open triggers retain hover styling. Disabled reason is a Pathfinder extension: retain action name, expose description, and allow focus without activation where the native wrapper supports it. -
SPLIT-ACTION-01— Figma: one obvious default and two to five related alternatives; no tertiary/destructive default. Each half reacts independently. Main-only loading; trigger-only open styling. No default means a normal dropdown trigger. Avoid two SplitButtons side by side. Square inner corners, 1px join, rounded outer corners.