Light

Button Group

Groups related button actions together in a horizontal or vertical layout.

Spec · from metadata

When to use

  • Grouping related actions (e.g., split button with dropdown)
  • Toolbar-style button layouts
  • Segmented button rows (e.g., view mode: grid/list)
  • Combining buttons with inputs or selects

When not to use

  • Toggle state selection — use ToggleGroup instead
  • Navigation tabs — use Tabs instead
  • Unrelated buttons — use standard flex layout

Variants

PropValuesDefaultDescription
orientationhorizontalverticalhorizontalhorizontal joins left-to-right; vertical stacks top-to-bottom.

Anti-patterns

Avoid<div className="flex">
  <Button className="rounded-r-none">Save</Button>
  <Button className="rounded-l-none border-l-0">Options</Button>
</div>
Prefer<ButtonGroup>
  <Button variant="outline">Save</Button>
  <Button variant="outline">Options</Button>
</ButtonGroup>

ButtonGroup handles border-radius removal and border collapsing automatically.

Accessibility

  • Screen readerRenders with role='group'. Each child button is independently accessible.

Token bindings

TokenCategoryUsage
mutedcolorButtonGroupText background
inputcolorButtonGroupSeparator color
rounded-mdradiusOuter corners border radius
shadow-xsshadowButtonGroupText shadow

Import

import { ButtonGroup } from "@timelycare/helix-ui"

Props

interface ButtonGroupProps extends React.HTMLAttributes<HTMLDivElement> {
  className?: string
  children: React.ReactNode
}

Styling

PropertyValueTailwind
DisplayInline flexinline-flex
Gap (nested groups)8pxgap-2
Border radius8pxrounded-md
Child border handlingAdjacent children share bordersFirst/last child get rounded corners, middle children get rounded-none

Border Collapse Behavior

Adjacent buttons within a ButtonGroup share borders — inner borders are collapsed so you don't get a double-border effect. The first child gets rounded-l-md, the last child gets rounded-r-md, and middle children get rounded-none.


Variants

ButtonGroup is a layout-only container. Visual styling comes from the child Button components and their variants.


Common Patterns

Basic Group

<ButtonGroup>
  <Button variant="outline">Cancel</Button>
  <Button variant="outline">Save</Button>
</ButtonGroup>

Nested Groups with Spacing

Nest ButtonGroup components to create sections with spacing between groups:

<ButtonGroup>
  <ButtonGroup>
    <Button variant="outline">Archive</Button>
    <Button variant="outline">Report</Button>
  </ButtonGroup>
  <ButtonGroup>
    <Button variant="outline">Snooze</Button>
    <Button variant="outline" size="icon" aria-label="More Options">
      <MoreHorizontalIcon />
    </Button>
  </ButtonGroup>
</ButtonGroup>

Split Button (with Dropdown)

<ButtonGroup>
  <Button variant="outline">Follow</Button>
  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <Button variant="outline" className="!pl-2">
        <ChevronDownIcon />
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuItem>Mute Conversation</DropdownMenuItem>
      <DropdownMenuItem>Mark as Read</DropdownMenuItem>
      <DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</ButtonGroup>

With Input (Prefix/Suffix)

<ButtonGroup>
  <Input placeholder="Search..." />
  <Button>Search</Button>
</ButtonGroup>

With Select and Input

<ButtonGroup>
  <ButtonGroup>
    <Select value={currency} onValueChange={setCurrency}>
      <SelectTrigger className="font-mono">{currency}</SelectTrigger>
      <SelectContent>
        <SelectItem value="$">$ US Dollar</SelectItem>
        <SelectItem value="€">€ Euro</SelectItem>
      </SelectContent>
    </Select>
    <Input placeholder="10.00" />
  </ButtonGroup>
  <ButtonGroup>
    <Button aria-label="Send" size="icon" variant="outline">
      <ArrowRightIcon />
    </Button>
  </ButtonGroup>
</ButtonGroup>

Accessibility

  • ButtonGroup is a purely visual container — no ARIA role required
  • Individual buttons retain their own accessibility attributes
  • Use aria-label on icon-only buttons within the group
  • For split button patterns, the dropdown trigger should have aria-label="More Options" or similar

Gotchas

ProblemSolution
Double borders between buttonsButtonGroup handles border collapse automatically; ensure buttons use variant="outline"
Corner radius on middle buttonsMiddle children automatically get rounded-none; don't override
Split button dropdown misalignedUse align="end" on DropdownMenuContent
Spacing between groups missingNest ButtonGroup inside another ButtonGroup for automatic gap
Input not flush with buttonBoth must be direct children of the same ButtonGroup

See Also


Last updated: February 19, 2026