# Button Group

Joins related buttons, inputs and labels into one control with shared borders.

```tsx
import { Button } from "@zenoxai/ui/components/button"
import { ButtonGroup } from "@zenoxai/ui/components/button-group"

function ButtonGroupDemo() {
  return (
    <ButtonGroup aria-label="Alert actions">
      <Button variant="outline">Acknowledge</Button>
      <Button variant="outline">Assign</Button>
      <Button variant="outline">Resolve</Button>
    </ButtonGroup>
  )
}
```

## Usage

```tsx
import { Button } from "@zenoxai/ui/components/button"
import { ButtonGroup } from "@zenoxai/ui/components/button-group"

export function AlertActions() {
  return (
    <ButtonGroup aria-label="Alert actions">
      <Button variant="outline">Acknowledge</Button>
      <Button variant="outline">Assign</Button>
      <Button variant="outline">Resolve</Button>
    </ButtonGroup>
  )
}
```

Use a button group for actions that act on the same object and belong together, such as the
actions on an alert, or to attach a button to an input. The group only changes layout: it
squares the inner corners and merges the borders of its direct children.

Use something else when:

- The buttons hold a selection state (a layout switch, a time range): use a
  [Toggle Group](/components/toggle-group).
- There are more than three or four actions: keep the main one visible and move the rest
  into a [Dropdown Menu](/components/dropdown-menu).

Use the same `variant` and `size` for every button in a group. Mixed heights break the shared
borders.

## Examples

### Nested

A `ButtonGroup` inside another adds an 8px gap between the inner groups. Use it for toolbars
made of several clusters, like pagination.

```tsx
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { ButtonGroup } from "@zenoxai/ui/components/button-group"

function ButtonGroupNested() {
  return (
    <ButtonGroup aria-label="Pagination">
      <ButtonGroup>
        <Button variant="outline" size="icon" aria-label="Previous page">
          <ChevronLeftIcon />
        </Button>
        <Button variant="outline" size="icon" aria-label="Next page">
          <ChevronRightIcon />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">1</Button>
        <Button variant="outline">2</Button>
        <Button variant="outline">3</Button>
      </ButtonGroup>
    </ButtonGroup>
  )
}
```

### Split button

A main action with a menu of variations next to it. `ButtonGroupSeparator` draws the divider
between two filled buttons, which have no border of their own.

```tsx
import { ChevronDownIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { ButtonGroup, ButtonGroupSeparator } from "@zenoxai/ui/components/button-group"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@zenoxai/ui/components/dropdown-menu"

function ButtonGroupSplit() {
  return (
    <ButtonGroup>
      <Button>Export report</Button>
      <ButtonGroupSeparator />
      <DropdownMenu>
        <DropdownMenuTrigger
          render={<Button size="icon" aria-label="More export formats" />}
        >
          <ChevronDownIcon />
        </DropdownMenuTrigger>
        <DropdownMenuContent align="end" className="w-40">
          <DropdownMenuItem>Export as PDF</DropdownMenuItem>
          <DropdownMenuItem>Export as CSV</DropdownMenuItem>
          <DropdownMenuItem>Export as JSON</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </ButtonGroup>
  )
}
```

### With an input

An `Input` inside the group stretches to fill the row. `ButtonGroupText` renders a static
prefix or suffix, such as a protocol or a unit, with the same borders.

```tsx
import { SearchIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { ButtonGroup, ButtonGroupText } from "@zenoxai/ui/components/button-group"
import { Input } from "@zenoxai/ui/components/input"

function ButtonGroupWithInput() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <ButtonGroup className="w-full">
        <Input aria-label="Search domains" placeholder="Search domains" />
        <Button variant="outline" size="icon" aria-label="Search">
          <SearchIcon />
        </Button>
      </ButtonGroup>
      <ButtonGroup className="w-full">
        <ButtonGroupText>https://</ButtonGroupText>
        <Input aria-label="Domain" placeholder="login.acme-corp.com" />
      </ButtonGroup>
    </div>
  )
}
```

### Vertical

`orientation="vertical"` stacks the children and merges their top and bottom borders, for
compact controls such as map zoom.

```tsx
import { MinusIcon, PlusIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { ButtonGroup } from "@zenoxai/ui/components/button-group"

function ButtonGroupVertical() {
  return (
    <ButtonGroup orientation="vertical" aria-label="Map zoom">
      <Button variant="outline" size="icon" aria-label="Zoom in">
        <PlusIcon />
      </Button>
      <Button variant="outline" size="icon" aria-label="Zoom out">
        <MinusIcon />
      </Button>
    </ButtonGroup>
  )
}
```

## Accessibility

- `ButtonGroup` renders `role="group"`. Give it an `aria-label` when the purpose is not clear
  from the buttons alone ("Alert actions", "Pagination").
- Every button keeps its own tab stop; the group adds no arrow-key navigation. For a single
  tab stop with arrow keys, use a [Toggle Group](/components/toggle-group).
- Icon-only buttons need an `aria-label`. `ButtonGroupText` is plain text: an input next to it
  still needs its own label.

## API reference

`import { ... } from "@zenoxai/ui/components/button-group"`

### ButtonGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| orientation | `"horizontal" \| "vertical" \| null` | `horizontal` | `horizontal` (default) joins the items side by side; `vertical` stacks them. |

### ButtonGroupSeparator

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| orientation | `"horizontal" \| "vertical"` | `vertical` | The orientation of the separator. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, SeparatorState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

### ButtonGroupText

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

