Button Group

A set of related buttons arranged as a visual unit. Supports attached (fused pill) and detached (spaced) layouts, with shared variant, size, and density propagated via context.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add button-group

Usage#

usage.tsxTSX
import { ButtonGroup, ButtonGroupItem } from "@/components/ui/button-group"export function Example() {  return (    <ButtonGroup>      <ButtonGroupItem>Day</ButtonGroupItem>      <ButtonGroupItem>Week</ButtonGroupItem>      <ButtonGroupItem>Month</ButtonGroupItem>    </ButtonGroup>  )}

Anatomy#

import { ButtonGroup, ButtonGroupItem } from "@/components/ui/button-group"<ButtonGroup>  <ButtonGroupItem />  <ButtonGroupItem /></ButtonGroup>

Attached vs Detached#

By default, attached fuses buttons into a single pill: interior border radii are stripped and every item after the first drops its left border, so neighbours share one 1px stroke instead of stacking two. Set attached={false} for a spaced layout where each button keeps its own rounding.

Variants#

The variant prop on ButtonGroup is inherited by every ButtonGroupItem inside it. Supports all the same variants as Button.

Sizes#

Inherits Button's sizes - xs (28px, for a toolbar of row actions in a dense table), sm (32px), md (36px, the default), and lg (40px). The marketing xl is not offered on a group.

Density#

With no size, density picks the height: compact is sm, comfortable is md. An explicit size still wins. Like a Button, the group ignores an ambient DensityProvider and stays md. See Density.

Icon-only#

Set iconOnly on each item to collapse it to a square. Always pass an aria-label - Koala warns in development if one is missing.

Per-item overrides#

Any prop set directly on a ButtonGroupItem overrides the group context. Mix variants within the same group to give one action more visual weight - keep all items the same surface style (both bordered, or both filled) so the joined edges look intentional.

API reference#

ButtonGroup

A <div role="group"> whose variant and size flow to every item. Forwards all native <div> props. Every part accepts className, merged last.

PropTypeDefaultDescription
attachedbooleantrueFuses the items into one pill: inner corners squared, neighbours sharing one stroke. false spaces them with a 4px gap.
variant"primary" | "secondary" | "neutral" | "outline" | "ghost" | "overlay" | "destructive" | "destructiveGhost" | "link""outline"The Button variant every item takes.
size"xs" | "sm" | "md" | "lg""md"The height every item takes: 28, 32, 36 or 40px. Unset, it follows density, then the size a container imposes.
density"comfortable" | "compact"-Picks the size when none is set: compact is sm, comfortable is md. An explicit size wins.

ButtonGroupItem

One button in the group. Forwards all native <button> props.

PropTypeDefaultDescription
variant"primary" | "secondary" | "neutral" | "outline" | "ghost" | "overlay" | "destructive" | "destructiveGhost" | "link"-Overrides the group's variant for this item.
size"xs" | "sm" | "md" | "lg"-Overrides the group's size for this item.
density"comfortable" | "compact"-Overrides the group's density for this item; beats the group's size too.
iconOnlybooleanfalseA square icon button. Pass aria-label; development warns without one.
asChildbooleanfalseRenders its single child as the button (Radix Slot).

FAQ#

Use ButtonGroup when several related actions read as one unit, like a Day/Week/Month switch or a formatting toolbar. It shares variant, size, and density across items via context and, when attached, fuses them into a single pill. For unrelated actions, keep them as standalone Buttons.