Split button

A primary action with a menu of nearby alternatives.

actionmenu
Preview
Split button preview

When to use

  • One default action with a few close variants, like Merge with Squash and Rebase.
  • Export or share actions where one format is the usual pick and others sit behind the chevron.
  • Copy actions that swap the label to Copied in place while offering alternatives.

When not to use

  • Use dropdown-menu when there is no default action and every option is equal.
  • Use button when there are no alternatives.
  • Use context-menu for actions tied to a piece of content rather than a toolbar.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "split-button",
  "type": "registry:ui",
  "title": "Split button",
  "description": "A primary action with a menu of nearby alternatives.",
  "categories": [
    "actions"
  ],
  "dependencies": [
    "@radix-ui/react-dropdown-menu",
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/split-button/split-button.tsx",
      "type": "registry:component",
      "target": "@components/manicat/split-button/split-button.tsx"
    },
    {
      "path": "registry/components/split-button/split-button.module.css",
      "type": "registry:component",
      "target": "@components/manicat/split-button/split-button.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/split-button",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/split-button",
    "markdown": "https://uiarc.dev/components/split-button/markdown",
    "tags": [
      "action",
      "menu"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { useEffect, useState } from "react";
import { SplitButton } from "@/components/manicat/split-button/split-button";

export function SplitButtonExample() {
  const [count, setCount] = useState(0);

  return (
    <SplitButton
      label={`Publish (${count})`}
      onClick={() => setCount((value) => value + 1)}
      actions={[{ label: "Publish now" }, { label: "Schedule for later" }, { label: "Publish to a copy" }]}
    />
  );
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Publish options The main action stays one click; the menu holds the variants.

Publish options The main action stays one click; the menu holds the variants. preview

Nothing published yet

Keyboard interactions

KeysAction
Enter / SpaceRuns the main action, or opens the menu from the chevron.
ArrowDown / ArrowUpMoves between menu items, looping at the ends.
EscapeCloses the menu and returns focus to the chevron.

Accessibility

  • Two native buttons; the chevron is labelled "<label> more actions".
  • The menu is a Radix DropdownMenu with menu and menuitem roles and focus management.
  • The main label is announced through a polite live region when it changes.

Motion

  • The main label and icon morph in place while its width springs; the menu half never scales, so the menu opens from a still anchor.
  • Menu items fade in with a short stagger.
  • Reduced motion removes the press scale, width spring, and menu transform, leaving a quick opacity fade.

Responsive behavior

  • The menu opens aligned to the end of the button with 12px collision padding, so it stays on screen near viewport edges.
  • The secondary variant sets a 142px minimum on the main half; the pair does not collapse on narrow screens, so give it its own row on mobile.

Performance

  • The main label animates per letter with a ResizeObserver-driven width spring; the menu mounts only while open through a Radix portal.

Notes for AI

  • Use when one action is the default and two to five close variants exist. For a menu with no default action use dropdown-menu.
  • Keep the menu actions as variations of the main action; put unrelated commands elsewhere.
  • Swap label and icon (Copy page → Copied) to show the result in place.