Skip to content

Split Button

A split button combines a primary action button with a secondary trigger that opens a menu of related alternate actions.

Start with separate buttons

Split buttons are a high-complexity pattern. Default to using separate fd-button elements or a fd-button-group instead. Only reach for fd-split-button when all of the following are true:

  1. There is one clearly primary action that users will take most of the time.
  2. The menu items are variations of that same primary action (e.g., "Save" / "Save as Draft" / "Save & Close").
  3. The number of alternates is small (4 or fewer).
  4. Grouping the actions genuinely helps users — separate buttons would create clutter without improving clarity.

If any of these conditions is not met, use separate buttons.

Component

Use fd-split-button when you have a single primary action with a small set of clearly related alternates. The component renders a primary action segment alongside a trigger that opens an fd-menu containing fd-menu-item elements. Split buttons are a high-complexity pattern and should be used sparingly.

When to use

  • A single primary action has clearly related alternates — For example, "Save" with "Save as Draft" and "Save & Close," or "Download" with "Download as PDF" and "Download as CSV."
  • The primary action is obvious — The most common action should be immediately available without opening the menu. The alternates are less frequent variations of the same intent.
  • You need to conserve space — When separate buttons for each action would create visual clutter, but the actions are related enough to group.

When not to use

  • Don't use when the actions are not clearly related — If the menu items are conceptually different from the primary action, use separate buttons instead. A split button implies the menu contains variations of the primary action.
  • Don't use when there are many options — If you have more than four or five alternates, consider a standalone menu or select pattern instead. Split buttons work best with a small set of choices.
  • Don't use when the primary action is not obvious — If no single action is clearly more common than the others, use a button group or separate buttons so each action has equal visual weight.
  • Don't use for navigation — Split buttons are for actions, not for navigating to different pages.
  • When in doubt, prefer separate buttons — Split buttons introduce keyboard and focus complexity. Use them only when the grouping genuinely helps the user.

Examples

Split button variants and states. Open Storybook for interactive controls, disabled states, and placement options. View in Storybook →

Usage

html
<fd-split-button variant="primary" trigger-label="More save options">
  Save
  <fd-menu-item slot="menu">Save as Draft</fd-menu-item>
  <fd-menu-item slot="menu">Save & Submit for Review</fd-menu-item>
</fd-split-button>

With a leading icon:

html
<fd-split-button variant="primary" trigger-label="More save options">
  <fd-icon slot="icon-start" name="floppy-disk"></fd-icon>
  Save
  <fd-menu-item slot="menu">Save as Draft</fd-menu-item>
  <fd-menu-item slot="menu">Save & Submit for Review</fd-menu-item>
</fd-split-button>

Properties

NameTypeDefaultDescription
variant"primary" | "neutral" | "subtle" | "outline" | "destructive"primaryVisual variant applied to both segments
disabledbooleanfalseDisables both segments. The menu cannot open while disabled.
trigger-disabledbooleanfalseDisables only the trigger segment. The primary action remains active.
trigger-labelstring"More options"Accessible name for the trigger segment. Reflected public attribute. Should describe the menu contents contextually.
menu-placementPlacement"bottom-start"Preferred placement of the menu relative to the trigger. Reflected public attribute.
openbooleanfalseRead-only reflected state indicating whether the menu is open. Derived from internal fd-menu events. Do not set directly.

Slots

NameDescription
(default)Label content for the primary action segment
icon-startLeading icon for the primary segment (use fd-icon)
menufd-menu-item elements only. Items are adopted into the internal fd-menu on connection.

Events

NameDetailDescription
fd-split-button-action{}Fired when the primary segment is activated
fd-split-button-open-change{ open: boolean }Fired when the menu opens or closes

Compatibility note:

  • fd-split-button still fires deprecated fd-split-action and fd-split-open during the compatibility window.
  • New consumer code should listen to fd-split-button-action and fd-split-button-open-change.

CSS custom properties

NameDefaultDescription
--fd-split-button-divider-colorVariant-dependentColor of the divider between the primary and trigger segments
--fd-split-button-divider-width1pxThickness of the divider
--fd-split-button-trigger-width44pxWidth of the trigger segment (minimum touch target)

Shadow parts

NameDescription
containerOuter wrapper containing both segments
primaryThe primary action button element
triggerThe menu trigger button element
dividerThe visual divider between the two segments

Accessibility

Keyboard behavior

Primary segment:

KeyBehavior
Enter / SpaceFires fd-split-button-action
TabMoves focus to the trigger segment

Trigger segment:

KeyContextBehavior
Enter / SpaceMenu closedOpens the menu
Enter / SpaceMenu openCloses the menu
ArrowDownMenu closedOpens the menu and focuses the first item
ArrowUpMenu closedOpens the menu and focuses the last item
EscapeMenu openCloses the menu and returns focus to the trigger (handled by fd-menu)
TabMenu openCloses the menu and moves focus naturally (handled by fd-menu)

Screen reader naming

  • The primary segment is named by its slotted text content (e.g., "Save").
  • The trigger segment requires trigger-label for its accessible name. Use a contextual label that describes the menu contents, such as "More save options" rather than just a caret symbol.

ARIA attributes

  • The trigger button has aria-haspopup="menu" to indicate it opens a menu.
  • The trigger button has aria-expanded reflecting the current open state.
  • The trigger button uses aria-label set from the trigger-label property.
  • The caret icon inside the trigger is aria-hidden="true".

Focus management

  • Each segment is independently focusable with its own :focus-visible ring.
  • When the menu closes (via Escape, item selection, or outside click), focus returns to the trigger segment. This is handled by fd-menu.
  • When disabled or trigger-disabled becomes true while the menu is open, the menu closes automatically.

DOM adoption

Menu items provided in the menu slot are adopted into the internal fd-menu after connection. They leave the consumer's light DOM and become children of the component's internal menu. This is the documented contract — do not rely on the items remaining in your light DOM after the component connects.

Content guidelines

  • Primary label should be the most common action. The visible button text should name the action users will take most often. Less frequent alternates go in the menu.
  • Name the trigger contextually. Set trigger-label to describe what the menu contains — "More save options," "Other export formats," "Additional actions." Avoid generic labels like "More" or leaving it as just a caret.
  • Menu items should be clearly related alternates. Every item in the menu should be a variation of the primary action's intent. If an item feels unrelated, it belongs elsewhere.
  • Use sentence case for all labels. Both the primary label and menu items should use sentence case for readability.

Known limitations

  • loading / loading-label is not supported in v1 — Loading state is gated on loading state pattern maturity across the system.
  • fd-menu-item only in v1 — The menu slot accepts only fd-menu-item elements. Arbitrary content, grouped items, and separators are not supported.
  • Form submission is not supportedfd-split-button is not form-associated. Use native form controls for submit/reset behavior.
  • trigger-label is shared with the internal menu label — The trigger's aria-label and the internal fd-menu's label attribute use the same value. This is an accessibility compromise for a smaller API surface in v1.