Skip to content
  1. Keyline UI
  2. Split button
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Split button

The usual action and its alternatives in one frame. The action runs with one press; the caret opens a menu that docks to the whole frame.

Loading the live example…

"use client"

import { useState } from "react"
import { MenuItem, MenuSeparator } from "@/components/keyline/ui/menu"
import { SplitButton, SplitButtonAction, SplitButtonMenu } from "@/components/keyline/ui/split-button"

export function BookDelivery() {
  const [booked, setBooked] = useState<string | null>(null)

  return (
    <div className="grid gap-4">
      <p className="m-0 text-[14px]">
        Order <span className="font-kl-mono whitespace-nowrap">DM-4120</span> · Wilkes-Barre Library · 6 cases, recycled bond
      </p>
      <SplitButton variant="primary" className="justify-self-start">
        <SplitButtonAction onClick={() => setBooked("Booked on Darryl’s next truck, today at 2 PM.")}>Book delivery</SplitButtonAction>
        <SplitButtonMenu label="Other ways to deliver">
          <MenuItem onClick={() => setBooked("Booked for tomorrow, 8 AM. The library opens at 9.")}>Tomorrow morning</MenuItem>
          <MenuItem onClick={() => setBooked("Held at the branch. Pam will call when it’s at reception.")}>
            Customer pickup at the branch
          </MenuItem>
          <MenuSeparator />
          <MenuItem description="Half today, half on Friday" onClick={() => setBooked("Split: 3 cases today, 3 cases Friday.")}>
            Split across two trucks
          </MenuItem>
        </SplitButtonMenu>
      </SplitButton>
      <p role="status" className="m-0 min-h-5 text-[14px] text-kl-ink-2">
        {booked ?? "Not booked yet. The warehouse closes at 5."}
      </p>
    </div>
  )
}

Installation

npx shadcn@latest add @keyline-ui/split-button

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

The menu’s rows are Menu’s, which the split button installs with it.

import { MenuItem } from "@/components/keyline/ui/menu"
import { SplitButton, SplitButtonAction, SplitButtonMenu } from "@/components/keyline/ui/split-button"
<SplitButton variant="primary">
  <SplitButtonAction onClick={book}>Book delivery</SplitButtonAction>
  <SplitButtonMenu label="Other ways to deliver">
    <MenuItem onClick={bookTomorrow}>Tomorrow morning</MenuItem>
  </SplitButtonMenu>
</SplitButton>

Examples

Remember the last choice

The action can repeat whatever was chosen from the menu last, so the second export is one press.

Loading the live example…

"use client"

import { useState } from "react"
import { MenuItem } from "@/components/keyline/ui/menu"
import { SplitButton, SplitButtonAction, SplitButtonMenu } from "@/components/keyline/ui/split-button"

const FORMATS = ["PDF", "CSV", "Excel"] as const
type Format = (typeof FORMATS)[number]

export function ExportReport() {
  // the action repeats the last format chosen from the menu
  const [format, setFormat] = useState<Format>("PDF")
  const [file, setFile] = useState<string | null>(null)

  const exportAs = (f: Format) => {
    setFormat(f)
    setFile(`scranton-q3-sales.${f === "Excel" ? "xlsx" : f.toLowerCase()}`)
  }

  return (
    <div className="grid gap-4">
      <SplitButton className="justify-self-start">
        <SplitButtonAction onClick={() => exportAs(format)}>Export as {format}</SplitButtonAction>
        <SplitButtonMenu label="Export formats">
          {FORMATS.map((f) => (
            <MenuItem key={f} onClick={() => exportAs(f)}>
              {f}
            </MenuItem>
          ))}
        </SplitButtonMenu>
      </SplitButton>
      <p role="status" className="m-0 min-h-5 text-[14px] text-kl-ink-2">
        {file ? (
          <>
            Saved <span className="font-kl-mono">{file}</span> to Downloads.
          </>
        ) : (
          "Q3 sales report, Scranton branch."
        )}
      </p>
    </div>
  )
}

Props

PropTypeDefaultDescription
variant"secondary" | "primary"Default "secondary"Primary floods both cells cobalt.
layout"control" | "cell"Default "control"cell drops the frame, for a split button inside a bar or a panel footer.
labelstringDefault requiredOn SplitButtonMenu: the caret’s accessible name. Say what the menu holds.

Parts: SplitButton, SplitButtonAction (a button; its props pass through), SplitButtonMenu. The menu is Base UI Menu.

Sheet 9 of 73PreviousButton groupNextToggle
On this page