Toggle Group

A set of pressable pills that hold a selection, built on Radix ToggleGroup. Use single for a one-of-N choice or multiple for independent toggles. A chosen pill carries the ink outline.

Installation#

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

Usage#

usage.tsxTSX
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"export function Example() {  return (    <ToggleGroup type="single" defaultValue="yes">      <ToggleGroupItem value="yes">Yes</ToggleGroupItem>      <ToggleGroupItem value="no">No</ToggleGroupItem>    </ToggleGroup>  )}

Anatomy#

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"<ToggleGroup type="single">  <ToggleGroupItem value="…" /></ToggleGroup>

Single vs. multiple#

type="single" is a mutually exclusive choice: selecting one pill clears the rest. type="multiple" lets each pill toggle on and off independently, like a formatting band.

type="single"
type="multiple"

Variants#

outline (the default) gives each pill its own border on the page ground, and the chosen one takes the ink outline. Use it when the pills are independent choices. segmented puts them in one muted track and lifts the chosen pill off it instead, the same elevation the Tabs pill indicator uses. Reach for it when the set reads as a single control, such as the format switch in the Color Picker. The lift is one sliding thumb rather than a background on each pill, so choosing another option travels instead of blinking: it snaps into place on load and slides only on later changes. ghost drops the pill altogether: bare labels in the foreground, the chosen one underlined, for a dense set where a frame per option would read as a wall of boxes.

outline
segmented
ghost

Size grid#

The ghost variant is the size picker on a product page: lay the group out as a grid and let the cells shed their side padding. Weight stays medium when a size is chosen, so a bolder label never nudges its neighbours. A sold-out size stays in the grid, disabled, its label wrapped in <s> and its aria-label saying so.

Sizes#

sm (32px) is the compact rating pill; md (40px) is the default. Set size on the group and every item inherits it through context.

sm
md

Icon-only with tooltips#

iconOnly squares an item around its glyph (32px at sm, 40px at md, where the glyph steps up to 20px), the same square the lone Toggle draws. A glyph is not a name, so an icon-only item always needs a hint, and tooltip gives it one with the same wiring as Button: a string tooltip is shown on hover and focus and also names an item that has no aria-label, and an icon-only item with an aria-label shows that label as its hint on its own. On a labelled pill a tooltip is a supplementary hint. Pass tooltip={false} to opt out when you wrap the item in your own Tooltip.

named by its tooltip
hint from the aria-label
a hint on a labelled pill

Feedback card#

The classic "Was this useful?" prompt: a title, a question, and a Yes/No band on a card. Rendered open so it reads at a glance, with the group controlled so the selection is yours to send.

Was this useful?

How would you rate your overall experience?

Inline feedback#

The compact variant: just the question and a Yes/No band, no subtitle. Answering swaps the prompt for a thank-you, so the widget always feels resolved.

Was this useful?

Inside a popover#

The same content floated: drop the card into a Popover when the prompt should appear on demand rather than sit in the page.

API reference#

ToggleGroup

The group root, with roving focus across its items. Forwards all Radix ToggleGroup Root props, including ref; every part accepts className, merged last.

PropTypeDefaultDescription
type*"single" | "multiple"-single holds one value (clicking the chosen item clears it); multiple toggles each item independently.
valuestring | string[]-The controlled value: a string for single, a string[] for multiple.
defaultValuestring | string[]-The initial value when uncontrolled.
onValueChange(value: string) => void | (value: string[]) => void-Called when the selection changes, with the shape matching type.
variant"outline" | "segmented" | "ghost""outline"Free-standing pills with an ink outline, one muted track with a sliding thumb, or bare labels with an underline.
size"sm" | "md""md"32px or 40px items, shared with every item through context.
disabledbooleanfalseDisables every item.
rovingFocusbooleantrueArrow keys move focus between items.
loopbooleantrueArrow-key focus wraps from the last item to the first.
orientation"horizontal" | "vertical"-The arrow keys roving focus listens to; it does not change the layout.
Data attributeDescription
data-orientationThe orientation, when set.
data-thumbsegmented only: "on" while the sliding thumb shows (exactly one item on), "off" when items paint their own lift.
CSS variableDescription
--surfaceRead by outline items for their fill, so they blend into the container; falls back to --background.

ToggleGroupItem

One pressable pill. Forwards all Radix ToggleGroup Item props.

PropTypeDefaultDescription
value*string-The item's value in the group.
disabledbooleanfalseDisables this item.
iconOnlybooleanfalseA square pill around one glyph (20px at md). Name it with aria-label or a string tooltip.
tooltipReact.ReactNode | false-A hint on hover and focus; defaults to the aria-label on an iconOnly item, and a string names an unlabelled one. false opts out.
tooltipPlacementTooltipProps["placement"]"top"Where the tooltip sits.
Data attributeDescription
data-state"on" while selected, "off" otherwise.
data-disabledPresent when the item is disabled.
data-orientationThe group's orientation, when set.

FAQ#

Use ToggleGroup when one or several options from a small, visible set should look chosen, like a rating or a view switch. Unlike RadioGroup the control is its own label, so no paired inputs are needed, and unlike Switch it can hold a one-of-N choice with `type="single"`.