ComponentsFoundations

Button Group & Toggle Group

ButtonGroup merges related buttons into one control — a split Publish ▾, pager arrows, a search field with its button. ToggleGroup adds a selected state with roving focus — view switches, chart ranges, text alignment and formatting. Toggle is a single pressed/unpressed button.

Radix Toggle Group (ToggleGroup, Toggle); ButtonGroup is plain role=group components/button-group.tsx, components/toggle.tsx, styles/forms.csstheme · material · accent · density · font

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { Button, ButtonGroup, ToggleGroup, ToggleGroupItem } from "@vamos-ui/react";

<ButtonGroup aria-label="Post actions">
  <Button>Preview</Button>
  <Button>Edit</Button>
</ButtonGroup>

<ToggleGroup aria-label="View" defaultValue="board">
  <ToggleGroupItem value="list">List</ToggleGroupItem>
  <ToggleGroupItem value="board">Board</ToggleGroupItem>
</ToggleGroup>

ButtonGroup has no roving focus: every button is its own tab stop. Use ToggleGroup when the buttons are a set of states.

Choosing: a form value → RadioGroup (segmented); a view or toolbar state → ToggleGroup; independent actions → ButtonGroup.

Examples

Split button

A primary group with ButtonGroupSeparator, so the merged edge stays visible on the accent fill. The chevron opens a Dropdown Menu.

With an input

Inputs and ButtonGroupText segments merge the same way; the focused child is raised so its ring isn't clipped.

https://
.vamos.site

Pager

variant and size on the group apply to direct Button / IconButton children that don't set their own.

21–40 of 340

Vertical

orientation="vertical" — map zoom controls. attached={false} keeps a gap instead of merging.

View switch (ToggleGroup)

The default "segmented" look: an inset track and a raised thumb. Single mode never empties unless allowEmpty is set.

Editor toolbar

type="multiple" for B / I / U, "outline" for attached alignment buttons, and a lone Toggle. Icon-only items take a label.

API

ButtonGroup

PropTypeDefault
attached
Merge borders and corners; false keeps an 8px gap.
booleantrue
orientation
Direction; corners merge with logical properties (RTL-safe).
"horizontal" | "vertical""horizontal"
variant
Default for direct Button / IconButton children.
Button variant—
size
Default size for direct Button / IconButton children.
"xs" | "sm" | "md" | "lg"—
aria-label
Recommended: names the group.
string—

ButtonGroupSeparator

PropTypeDefault
orientation
Hairline between attached buttons that share a fill.
"vertical" | "horizontal""vertical"

ButtonGroupText

PropTypeDefault
size
A static segment ("21–40 of 340", a prefix or unit).
"xs" | "sm" | "md" | "lg""md"

ToggleGroup

PropTypeDefault
type
One active item, or independent pressed states.
"single" | "multiple""single"
value / defaultValue
Active item(s).
string | string[]—
onValueChange
Called with the next value.
(value) => void—
allowEmpty
Single only: allow deselecting the active item.
booleanfalse
variant
Inset track + thumb, attached bordered buttons, or loose toolbar buttons.
"segmented" | "outline" | "ghost""segmented"
size
md follows --control-h.
"xs" | "sm" | "md" | "lg""md"
orientation
Layout and arrow-key axis.
"horizontal" | "vertical""horizontal"
block
Full width, items share the space.
boolean—
rovingFocus / loop
Arrow-key navigation behaviour.
booleantrue

ToggleGroupItem / Toggle

PropTypeDefault
value
ToggleGroupItem only. Required.
string—
label
Accessible name + tooltip for icon-only items.
string—
pressed / defaultPressed / onPressedChange
Toggle only: aria-pressed state.
boolean—
variant
Toggle only.
"ghost" | "outline""ghost"
size
Toggle only (items take the group size).
"xs" | "sm" | "md" | "lg""md"
disabled
Skipped by arrow keys.
boolean—