# Button Group

> A React button group: buttons joined into one control or spaced in a row, plus a split button that pairs a main action with a menu.

- Type: React component (Buttons & Actions)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add button-group`
- Docs: https://wingo-ui.com/components/button-group

## Interaction

Each button presses on its own spring inside the shared shape, and a focused one rises above its neighbours so the ring is never cut off. Tab moves from button to button. The chevron half of a split button opens its menu with a click, Enter, Space or ArrowDown, and turns while open; on a phone the menu arrives as an action sheet. Size, variant, tone, radius and color set on the group flow into every button that did not set its own.

## Installation

```bash
npx wingo-ui@latest add button-group
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/button-group` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/button-group.tsx`

npm dependencies: `npm i lucide-react`

Wingo UI dependencies (installed automatically):

- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) ([Markdown](https://wingo-ui.com/components/dropdown-menu.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Eye, Share2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup, SplitButton } from "@/components/ui/button-group"

export function PublishBar({ onPublish }: { onPublish: () => void }) {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <ButtonGroup aria-label="Preview and share">
        <Button leftIcon={<Eye />}>Preview</Button>
        <Button leftIcon={<Share2 />}>Share</Button>
      </ButtonGroup>
      <SplitButton
        tone="brand"
        onClick={onPublish}
        menuLabel="More publish options"
        menuProps={{ title: "Publish listing" }}
        items={[
          { label: "Save as draft" },
          { label: "Schedule publishing…" },
          { label: "Publish and duplicate" },
        ]}
      >
        Publish listing
      </SplitButton>
    </div>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children?` | `ReactNode` |  | Buttons, SplitButtons, ButtonGroupText cells and ButtonGroupSeparators. Other elements (a DropdownMenu with a Button trigger) still attach, but do not inherit the group's props: set them on the trigger. |
| `orientation?` | `"horizontal" \| "vertical"` | `"horizontal"` | A row, or a stack of full-width buttons (stacked actions on a phone). |
| `attached?` | `boolean` | `true` | Joined edges: inner corners squared, borders collapsed, dividers between solid, soft and ghost buttons, one shadow for the whole group. Off, the buttons sit in a spaced row (gap-2). |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Passed to every child that did not set one. Icon buttons stay square and follow along (sm becomes icon-sm, lg icon-lg). md grows to 44px on touch. |
| `variant?` | `"solid" \| "soft" \| "outline" \| "ghost"` | `"outline"` | Passed down. outline is the classic joined group; solid gets 1px dividers in the text color. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Passed down to every button. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Passed down; it is the outer shape of the whole group (full makes a pill). |
| `color?` | `string` | `""` | Any CSS color, passed down in place of the tone. Leave empty to use the tone. |
| `disabled?` | `boolean` | `false` | Disables every child that did not set disabled itself. |
| `fullWidth?` | `boolean` | `false` | The group fills its container and the buttons share the width. Made for phone footers. |
| `aria-label?` | `string` | `""` | The group's accessible name (role="group"). The playground falls back to a sample name when empty. |
| `className?` | `string` |  | Extra classes for the group, merged last. Other props go to the root div. |

## Parts

### SplitButton (component)

A main action joined to a chevron that opens a dropdown menu. Takes the Button props for its main half (onClick, leftIcon, loading, loadingText, disabled) plus items: MenuEntry[], menuLabel (default "More options"), menuIcon (default ChevronDown), align ("end") and side ("bottom"), menuProps for the rest of the menu (title of the phone sheet, responsive, shortcut) and fullWidth. The phone sheet is named after a string label unless menuProps sets one. Inside a full-width or vertical group it stretches its main half. variant / tone / size / radius / color default to solid / neutral / md / md and style both halves; loading disables the menu too. Props: SplitButtonProps.

### ButtonGroupText (component)

A non-interactive cell with a button's height, padding and type, for units and counters ("USD", "3 / 12"). Inherits from the group like a button. Props: ComponentProps<"div"> & { size, variant, tone, radius, color, disabled }.

### ButtonGroupSeparator (component)

An explicit extra break (a 1px line). Pass the group's orientation. Not needed between buttons: the attached group draws its own dividers. Props: ComponentProps<"div"> & { orientation?: "horizontal" | "vertical" }.

## Examples

- **Attached and spaced**: Joined into one shape or a spaced row, horizontal or stacked.
- **Pagination**: Icon buttons around a ButtonGroupText counter.
- **Quantity and units**: A pill stepper and a price with its currency cell.
- **Solid with dividers**: One shadow for the group and a 1px divider in the text color between buttons.
- **Split buttons**: Three sizes, an outline one, and loading, which blocks the menu too. On a phone the menu is a sheet.
- **Phone footer**: fullWidth shares the width between the buttons; a vertical group stacks actions, split buttons included.
- **Custom color**: Any CSS color flows into every button of the group.

## See also

- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) ([Markdown](https://wingo-ui.com/components/dropdown-menu.md))
- [Toggle Group](https://wingo-ui.com/components/toggle-group) ([Markdown](https://wingo-ui.com/components/toggle-group.md))
- [Segmented Control](https://wingo-ui.com/components/segmented-control) ([Markdown](https://wingo-ui.com/components/segmented-control.md))

---

Canonical: https://wingo-ui.com/components/button-group
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
