Acronis UIKit
Components

ButtonGroup

A compact cluster of related icon-only actions sharing a single box, hairline-separated.

Usage

import { ButtonGroup, ButtonGroupItem } from '@acronis-platform/ui-react';

ButtonGroup joins two to about five related icon actions into one control: a view switcher, zoom in / out, text alignment. The shared box and separators are the claim that the actions belong together — for unrelated actions side by side, use separate ButtonIcons instead.

Two container styles: outlined (the default — 1px border, 4px radius) and inlined, which draws no chrome of its own, for a group sitting inside a surface that already has a border.

The group follows the WAI-ARIA toolbar pattern: it is a single Tab stop and the arrow keys move between items, wrapping at the ends unless you set loopFocus={false}. (Home/End are not supported — a limitation of the underlying Base UI toolbar primitive.) ButtonGroup and ButtonGroupItem are both polymorphic via Base UI's render prop.

Both parts are themed by the --ui-button-group-* tokens; the item glyph and the focus ring come from the shared --ui-glyph-on-surface-primary and --ui-focus-primary semantic tokens. Pass className on either part to layer extra utilities on top — it is merged with the component's own classes, so Tailwind conflicts resolve in your favour.

Label both levels

Items are icon-only, so they have no text to name them — give each an aria-label. Give the container one too, describing what the actions have in common. ButtonGroup intentionally ships no default: a generic fallback would be unlocalizable and tell a screen reader user nothing.

Not a segmented control

The active fill is transient activation feedback and releases on mouse-up. This component has no persistent selected state — for one option staying visibly chosen, use ToggleGroup, whose items expose aria-pressed.

Examples

A view switcher:

<ButtonGroup aria-label="View mode">
  <ButtonGroupItem aria-label="List view" onClick={showList}>
    <ListIcon size={16} />
  </ButtonGroupItem>
  <ButtonGroupItem aria-label="Grid view" onClick={showGrid}>
    <LayoutGridIcon size={16} />
  </ButtonGroupItem>
  <ButtonGroupItem aria-label="Table view" onClick={showTable}>
    <LayoutTableIcon size={16} />
  </ButtonGroupItem>
</ButtonGroup>

Borderless, for a surface that already draws its own chrome:

<ButtonGroup aria-label="Zoom" variant="inlined">
  <ButtonGroupItem aria-label="Zoom out" onClick={zoomOut}>
    <MagnifierMinusIcon size={16} />
  </ButtonGroupItem>
  <ButtonGroupItem aria-label="Zoom in" onClick={zoomIn}>
    <MagnifierPlusIcon size={16} />
  </ButtonGroupItem>
</ButtonGroup>

Disable a single action, or the whole group:

<ButtonGroupItem aria-label="Table view" disabled>
  <LayoutTableIcon size={16} />
</ButtonGroupItem>

<ButtonGroup aria-label="View mode" disabled>
  {/* every item is disabled */}
</ButtonGroup>

A disabled item behaves differently here than elsewhere in the library: it stays focusable and is marked aria-disabled rather than natively disabled, so the arrow keys land on it (click and Enter/Space are still suppressed). That is required rather than a preference — the roving tabindex parks the group's single tabindex="0" on the first item, and browsers skip a natively disabled button, so a natively disabled first item would take the whole group out of the tab order.

Separators normally need no configuration. Figma models item position as an order variant (first / middle / last), but it only selects whether the trailing separator is drawn — the component derives that from the item's position, so the group stays variadic and you never restate the DOM order. Being an inline-end border, the separator also mirrors correctly under dir="rtl".

There is one composition that derivation cannot survive: if you wrap each item in another element, every button becomes a :last-child of its own wrapper and all the separators vanish. order is the escape hatch for exactly that case — it replaces the derivation instead of layering on top of it:

<ButtonGroup aria-label="View mode">
  <Wrapper>
    <ButtonGroupItem aria-label="List view" order="first">
      <ListIcon size={16} />
    </ButtonGroupItem>
  </Wrapper>
  <Wrapper>
    <ButtonGroupItem aria-label="Grid view" order="last">
      <LayoutGridIcon size={16} />
    </ButtonGroupItem>
  </Wrapper>
</ButtonGroup>

Leave order unset in normal use — items as direct children is the intended composition, and the one Figma's slot models.

API Reference

ButtonGroup

Prop

Type

ButtonGroupItem

Prop

Type

Edit on GitHub

On this page