Split button
A primary action with a menu of nearby alternatives.
actionmenu
Preview
Split button preview
Live · keyboard ready
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
Live · keyboard ready
Nothing published yet
publish-options-the-main-action-stays-one-click-the-menu-holds-the-variants-.tsxtsx
Keyboard interactions
| Keys | Action |
|---|---|
| Enter / Space | Runs the main action, or opens the menu from the chevron. |
| ArrowDown / ArrowUp | Moves between menu items, looping at the ends. |
| Escape | Closes 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.