Pathfinderby Commenda
Components

Split button

One default action with related alternatives.

Usage

Example.tsx
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

SH
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

Loading

API reference

PropTypeBehavior
classNamestringAdditional classes for the component root.
size"sm"|"md"|"lg"—
loadingConfigLoadingConfigView fields—
leadingIconReactNode—
disabledReasonstring—
openConditionalboolean—
onOpenChangeConditional(open: boolean) => void—
defaultOpenboolean—
labelRequiredstringVisible field label. Supply an accessible name when it is omitted.
variant"primary"|"secondary"—
completionConfigCompletionConfigView fields—
menuConfigRequiredSplitButtonMenuConfigView fields—
loadingConfigObject fields
FieldTypeRequirement
isLoadingbooleanRequired
labelstringOptional
completionConfigObject fields
FieldTypeRequirement
isCompletebooleanRequired
labelstringRequired
iconReactNodeOptional
menuConfigObject fields
FieldTypeRequirement
groupsDropdownGroup[]Required
labelstringOptional
disabledbooleanOptional
disabledReasonstringOptional
sideSideOptional
alignAlignOptional
sideOffsetnumberOptional
portalContainerHTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | nullOptional

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.

On this page