ComponentsDropdownButton
DropdownButton
Open a menu of related actions from a button.
On this page
Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.
View registry filesImport the component into your page or component file.
import {
DropdownButton,
type DropdownButtonSelectableAction,
DropdownMenuItem,
DropdownMenuSection,
DropdownMenuSeparator,
} from "@dethink/components";Menu mode performs no direct action. Split mode keeps one fixed dominant action. Selectable mode deliberately separates choosing the next action from executing it.
Menu button
Selectable, menu, controlled, split, disabled, loading, destructive, placed, and Motion-configured action examples.
Choose now, run from the primary action later
No action selected yet.
Choosing a menu item updates the selected label and checkmark. Only a later press of the primary half invokes that action's handler.
One trigger, related actions
Split primary action and alternatives
The primary side runs only Save. The separately named chevron opens alternatives; both controls remain normal Tab stops.
Controlled open state and destructive action
Controlled state: closed
Explicit async policies
Whole-composite loading is the safe default. Primary-only loading is opt-in for alternatives that the product has explicitly declared safe while the primary action runs.
DropdownButton narrows a few often-confused interaction patterns.
- DropdownButton (menu)
- Use when one labelled button reveals a small set of related commands with similar importance. Selecting an item runs an action.
- DropdownMenu
- Use the lower-level primitive when the trigger or surrounding composition is product-specific, such as an icon-only row overflow menu.
- Select or Combobox
- Use when the user chooses a value that persists in a field. DropdownButton items are commands, not options, and the trigger label does not become the selected item.
- Toolbar
- Use for a command surface with toolbar semantics and roving arrow-key focus. DropdownButton is one normal Tab stop before its menu opens.
- Split button
- Use when a dominant direct action must remain beside a separately named alternatives trigger. The primary side never opens the menu, and both native buttons stay in normal Tab order.
- DropdownButton (selectable)
- Use when choosing one declared command should make it the later primary action. Choosing updates selection only; a separate primary activation runs the selected handler.
- ToggleGroup
- Use for persistent pressed state across peer controls. Selectable DropdownButton chooses one future command; it is not a compact toggle surface.
The existing DropdownMenu remains the semantic and behavioral owner.
Enter, Space, and supported Up/Down Arrow behavior open the menu. Arrow keys, Home/End, typeahead, disabled-item skipping, and item activation operate inside it. Escape closes the menu and returns focus to the trigger.
In split mode, Enter and Space on the primary side run only the direct action. The icon-only menu half requires a localizable menuLabel. Tab and Shift+Tab visit both native buttons in document order; Left/Right Arrow does not move between them.
Selectable mode keeps the same two-button anatomy. The menu uses single-selection menuitemradio semantics and a visible checkmark. Choosing with pointer or keyboard changes the selected action, closes the menu, and returns focus to the menu trigger without executing the handler. The primary half runs the selected handler on a later activation.
Menu surface presence and changed item feedback use primitives from motion/react. The none preset disables choreography; reduced motion keeps a short opacity affordance while removing transforms. Open, focus, destructive, and disabled meaning never depends on movement.
Loading protects the direct action without silently changing what that action means.
loading keeps the primary label readable, exposes aria-busy, and prevents duplicate primary activation. The default loadingBehavior="all" disables the complete composite while work is running.
loadingBehavior="primary" is an explicit opt-in. Use it only when every remaining menu item is safe during the running primary operation. Whole-composite, primary-only, and menu-only disabled states remain separately controllable.
Fixed split applications may update label, primaryIcon, and onPrimaryAction without changing the menu. Selectable mode instead derives all three from the chosen action descriptor. Persisting that choice across sessions remains application-owned.
The discriminated contract separates menu children, fixed split props, and selectable action descriptors so choosing and execution cannot be conflated accidentally.
| Prop | What it does | Default |
|---|---|---|
mode"menu" | "split" | "selectable" | Menu mode renders one trigger. Split mode fixes a primary action. Selectable mode derives the later primary action from one chosen action descriptor. | "menu" |
actionsreadonly DropdownButtonSelectableAction[] | Selectable-mode action ownership: stable ID, label, optional description/icon, disabled/destructive state, and execution handler. | Not set |
selectedActionId / defaultSelectedActionId / onSelectedActionChangestring / string / (actionId) => void | Controlled or uncontrolled selectable state. Choosing updates the primary action but never invokes its handler. | Not set |
menuLabel / onPrimaryAction / primaryIconstring / Button onClick / ReactNode | menuLabel separately names the split/selectable menu half. onPrimaryAction and primaryIcon belong only to fixed split mode; selectable mode derives them from the chosen descriptor. | Not set |
loading / loadingBehaviorboolean / "all" | "primary" | Marks the readable primary action busy and prevents duplicate activation. The safe default disables both halves; primary keeps declared-safe menu alternatives available. | false / "all" |
disabled / primaryDisabled / menuDisabledboolean | Disable the complete composite or either native half independently. A disabled selected descriptor disables the selectable primary while leaving its menu available. | false |
labelReactNode | Visible trigger content and, when textual, the menu button's accessible name. Supply aria-label for non-text content. | Not set |
variant / sizeButtonVariant / ButtonSize | Uses the existing Button visual recipes without introducing a second variant system. | "outline" / "md" |
open / defaultOpen / onOpenChangeboolean / boolean / (open) => void | Controlled or uncontrolled menu visibility. | Not set |
placement / offset / crossOffset / containerPadding / shouldFlipDropdownMenu positioning props | Delegates logical placement and collision handling to DropdownMenu. Split mode anchors and sizes from the complete composite. | "bottom start" / 8 / 0 / 12 / true |
motionPreset"none" | "subtle" | "standard" | Selects Motion-based menu presence and item feedback. The user preference or reducedMotion removes transform choreography. | "standard" |
childrenReactNode | Menu and fixed split modes accept existing DropdownMenu item anatomy. Selectable mode rejects children because its action descriptors own selection and execution together. | Not set |
className / groupClassName / triggerClassName / contentClassName / menuClassNamestring | Compose classes at stable root, composite, trigger, surface, and menu slots. | Not set |