Phoundry UI

ButtonGroup

Groups related segments in a bordered shell (`secondary` raised or `outline`) with inset, rounded segments separated by a small gap.

import { ButtonGroup, type ButtonGroupItem, type ButtonGroupVariant } from 'phoundry-ui';

Toggle group

Show code
<ButtonGroup
  items={[
    { label: 'Day', active: view === 'day', onclick: () => (view = 'day') },
    { label: 'Week', active: view === 'week', onclick: () => (view = 'week') },
    { label: 'Month', active: view === 'month', onclick: () => (view = 'month') }
  ]}
/>

Outline variant

Show code
<ButtonGroup
  variant="outline"
  items={[
    { label: 'Day', active: outlineView === 'day', onclick: () => (outlineView = 'day') },
    { label: 'Week', active: outlineView === 'week', onclick: () => (outlineView = 'week') },
    { label: 'Month', active: outlineView === 'month', onclick: () => (outlineView = 'month') }
  ]}
/>

Small size

Show code
<ButtonGroup
  size="sm"
  items={[
    { label: 'List', active: listMode === 'list', onclick: () => (listMode = 'list') },
    { label: 'Grid', active: listMode === 'grid', onclick: () => (listMode = 'grid') }
  ]}
/>

Disabled, loading, stable keys

Optional id on each item stabilizes reconciliation when the list reorders. Loading shows the same spinner glyph as other buttons.

Show code
<ButtonGroup
  aria-label="Text alignment"
  items={[
    { id: 'left', label: 'Left', active: align === 'left', onclick: () => (align = 'left') },
    { id: 'center', label: 'Center', disabled: true },
    { id: 'right', label: 'Right', loading: true, onclick: () => (align = 'right') },
  ]}
/>

Props

PropTypeDefaultDescription
items requiredButtonGroupItem[]Segment entries: `label` (required) plus `icon`, `iconOnly`, `active`, `disabled`, `loading`, `class`, `onclick`, and other native `<button>` attributes.
variant 'secondary' | 'outline''secondary'`secondary` is the raised strip (default). `outline` matches `Button variant="outline"` shell styling (base fill, stronger outer border, radial highlight); the active segment is indicated by background only (`surface-overlay`).
size 'sm' | 'md''md'Height of the group; maps to the same scale as `Button` sm/md content.
class string''Additional classes on the outer `role="group"` container. Width is intrinsic by default (`w-fit`, like `ButtonDropdown` split). Add `w-full` if the group should stretch in a layout.
...rest HTMLAttributes<HTMLDivElement>Forwarded to the outer `role="group"` wrapper (`aria-label`, `data-*`, etc.).

Usage tips

  • Set active to reflect selection. With variant="secondary" (default), the active segment uses a surface-overlay fill. With variant="outline", styling aligns with Button variant="outline" for the shell; the active segment uses a surface-overlay fill only.
  • Icons, links, and the tooltip-based title pattern are not supported on group items; use a plain label string for each segment.