Light

Toggle Group

A set of two-state buttons that can be toggled on or off.

Spec · from metadata

When to use

  • Mutually exclusive option selection (type='single') like view mode or alignment
  • Multiple option selection (type='multiple') like text formatting toolbar
  • Segmented control patterns

When not to use

  • Single toggle — use Toggle instead
  • Radio-style form input — use RadioGroup instead
  • Tab navigation — use Tabs instead
  • Button grouping without toggle state — use ButtonGroup instead

Variants

PropValuesDefaultDescription
variantdefaultoutlinedefaultVisual style inherited from toggleVariants. Outline adds borders.
sizedefaultsmlgdefaultHeight of toggle items, inherited from toggleVariants.

Anti-patterns

Avoid<ToggleGroup>
  <ToggleGroupItem value="a">A</ToggleGroupItem>
</ToggleGroup>
Prefer<ToggleGroup type="single" defaultValue="a">
  <ToggleGroupItem value="a">A</ToggleGroupItem>
  <ToggleGroupItem value="b">B</ToggleGroupItem>
</ToggleGroup>

type prop is required. Always provide defaultValue for uncontrolled usage.

Avoid<ToggleGroup type="single">
  <ToggleGroupItem value="a" variant="outline">A</ToggleGroupItem>
  <ToggleGroupItem value="b" variant="default">B</ToggleGroupItem>
</ToggleGroup>
Prefer<ToggleGroup type="single" variant="outline">
  <ToggleGroupItem value="a">A</ToggleGroupItem>
  <ToggleGroupItem value="b">B</ToggleGroupItem>
</ToggleGroup>

Set variant on ToggleGroup, not individual items. Context propagates the value.

Accessibility

  • Required ARIAaria-label on the group
  • Min touch target32px
  • Screen readerBuilt on Radix ToggleGroup. Items announce pressed/unpressed state. Group manages roving tabindex.

Token bindings

TokenCategoryUsage
accentcolorSelected item background
accent-foregroundcolorSelected item text
inputcolorOutline variant border
rounded-mdradiusItem border radius

Import

import { ToggleGroup, ToggleGroupItem } from "@timelycare/helix-ui"

Props

interface ToggleGroupProps {
  type: "single" | "multiple"
  value?: string | string[]
  onValueChange?: (value: string | string[]) => void
  variant?: "default" | "outline"
  size?: "default" | "sm" | "lg"
  disabled?: boolean
  className?: string
  children: React.ReactNode
}

interface ToggleGroupItemProps {
  value: string
  disabled?: boolean
  className?: string
  children: React.ReactNode
}

Structure

PartTailwind
ToggleGroup (default)flex items-center gap-1
ToggleGroup (outline)flex items-center (items joined)
ToggleGroupIteminline-flex items-center justify-center gap-2 rounded-md text-sm font-semibold
Iconsize-4 (16px)

Variants

VariantContainerItem Styling
defaultgap-1 (4px between items)bg-transparentbg-accent when pressed
outlineItems joined, borders collapseborder border-input bg-background shadow-xs

Outline Border Radius

  • First item: rounded-l-md rounded-r-none
  • Middle items: rounded-none
  • Last item: rounded-r-md rounded-l-none
  • Overlap: mr-[-1px] to collapse borders

Sizes

SizeHeightPadding
smh-8 (32px)px-3
defaulth-9 (36px)px-2
lgh-10 (40px)px-2.5

States

StateItem Styling
Default (off)bg-transparent or bg-background (outline)
Hoverhover:bg-muted hover:text-muted-foreground
Pressed (on)bg-accent text-accent-foreground
Focusring-[3px] ring-ring/50
Disabledopacity-50 pointer-events-none

Common Patterns

Icon Only (Single Select)

<ToggleGroup type="single" variant="default">
  <ToggleGroupItem value="bold"><Bold className="size-4" /></ToggleGroupItem>
  <ToggleGroupItem value="italic"><Italic className="size-4" /></ToggleGroupItem>
  <ToggleGroupItem value="underline"><Underline className="size-4" /></ToggleGroupItem>
</ToggleGroup>

Text (Single Select with Outline)

<ToggleGroup type="single" variant="outline">
  <ToggleGroupItem value="all">All</ToggleGroupItem>
  <ToggleGroupItem value="missed">Missed</ToggleGroupItem>
</ToggleGroup>

Multiple Selection

<ToggleGroup type="multiple" variant="default">
  <ToggleGroupItem value="bold"><Bold className="size-4" /></ToggleGroupItem>
  <ToggleGroupItem value="italic"><Italic className="size-4" /></ToggleGroupItem>
</ToggleGroup>

Accessibility

  • Built on Radix ToggleGroup — handles keyboard and focus automatically
  • Keyboard: Arrow keys to navigate between items, Enter/Space to toggle
  • Screen reader: announces group with item pressed states
  • Uses roving tabindex for focus management
  • Single vs. multiple selection mode affects aria-pressed

Gotchas

ProblemSolution
Border double-thick between itemsUse mr-[-1px] on items (except last)
Wrong border radius on middle itemsApply rounded-none to middle items
Selection not workingEnsure type prop is set to "single" or "multiple"

See Also


Last updated: February 9, 2026