# Toggle Group

> A React toggle group: a row of toggles where one or several stay pressed, for formatting, alignment, view switches and filters.

- Type: React component (Buttons & Actions)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add toggle-group`
- Docs: https://wingo-ui.com/components/toggle-group

## Interaction

Click or tap an item and it stays pressed. In single mode the highlight slides over to the new item on a smooth spring and the old one lets go; in multiple mode each item pops its own highlight in and out. Every item dips a little while your finger is down. The group is one tab stop: arrow keys move along it (wrapping around unless loop is off), Home and End jump to the ends, Space or Enter presses. Icon-only items name themselves in a tooltip on hover or keyboard focus, with the shortcut hint next to the name; on a phone a long press shows it without pressing the item. On touch the medium size is 44px tall and the small one keeps a 44px hit area.

## Installation

```bash
npx wingo-ui@latest add toggle-group
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/toggle-group` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/toggle-group.tsx`

npm dependencies: `npm i @radix-ui/react-toggle-group motion`

Wingo UI dependencies (installed automatically):

- [Tooltip](https://wingo-ui.com/components/tooltip) ([Markdown](https://wingo-ui.com/components/tooltip.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { AlignCenter, AlignLeft, AlignRight, Bold, Italic, Underline } from "lucide-react"
import { ToggleGroup, ToggleGroupSeparator } from "@/components/ui/toggle-group"

export function EditorToolbar({ align, onAlignChange }: { align: string; onAlignChange: (value: string) => void }) {
  return (
    <div role="toolbar" aria-label="Editor" className="flex items-center gap-1">
      <ToggleGroup
        type="multiple"
        iconOnly
        defaultValue={["bold"]}
        aria-label="Formatting"
        options={[
          { value: "bold", label: "Bold", icon: <Bold />, shortcut: "mod+b" },
          { value: "italic", label: "Italic", icon: <Italic />, shortcut: "mod+i" },
          { value: "underline", label: "Underline", icon: <Underline />, shortcut: "mod+u" },
        ]}
      />
      <ToggleGroupSeparator />
      <ToggleGroup
        iconOnly
        value={align}
        onValueChange={onAlignChange}
        aria-label="Alignment"
        options={[
          { value: "left", label: "Left", icon: <AlignLeft /> },
          { value: "center", label: "Center", icon: <AlignCenter /> },
          { value: "right", label: "Right", icon: <AlignRight /> },
        ]}
      />
    </div>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | `"single" \| "multiple"` | `"single"` | single: one item stays pressed and the value is a string (an alignment, a view). multiple: any number stay pressed and the value is a string[] (bold, italic, filters). |
| `options?` | `ToggleGroupOption[]` |  | { value: string; label: ReactNode; icon?: ReactNode; disabled?: boolean; tooltip?: ReactNode; shortcut?: string }. The shortcut is a hint shown in the tooltip and set as aria-keyshortcuts; bind it yourself with useShortcut. Values that disappear from the list are dropped from the value. |
| `value?` | `string \| string[]` |  | Controlled value, typed by type: a string for single (empty for none), a string[] for multiple. |
| `defaultValue?` | `string \| string[]` |  | Starting value while uncontrolled: "" or [] when omitted. |
| `onValueChange?` | `(value: string \| string[]) => void` |  | Fires with the new value, typed by type: a string for single ("" when cleared), a string[] for multiple. |
| `deselectable?` | `boolean` | `false` | single only: clicking the pressed item clears the value. Off by default, since an alignment or a view always has one. |
| `variant?` | `"ghost" \| "outline" \| "soft"` | `"ghost"` | ghost: transparent items for toolbars, pressed ones get the selected tint. outline: items joined into one bordered control with hairlines between them. soft: a tinted track where the pressed item becomes a solid fill. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Height 32 / 40 / 48px with matching padding, type and icon size; icon-only items are square. On touch screens md grows to 44px and sm keeps a 44px hit area. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corner shape, scaled with the size like the button's. full turns the group into a pill. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Color of the pressed items. neutral is the gray selected tint (a black or white fill on soft); the other tones tint the highlight and color the text. |
| `color?` | `string` | `""` | Any CSS color for the pressed items; replaces the tone. Leave empty to use the tone. |
| `orientation?` | `"horizontal" \| "vertical"` | `"horizontal"` | The direction of the row and of the arrow keys. Vertical items align their labels to the start. |
| `iconOnly?` | `boolean` | `false` | Shows only the icons in square items. Each label becomes the item's aria-label and its tooltip (with the shortcut hint). |
| `fullWidth?` | `boolean` | `false` | Items share the width of the container, the usual layout for a view switch on a phone. |
| `attached?` | `boolean` | `true` | outline only: items share their borders as one control. Off: they stand apart as separate chips with a gap; add flex-wrap through className to let them wrap. |
| `loop?` | `boolean` | `true` | Arrow keys wrap from the last item to the first and back. Off: they stop at the ends. |
| `disabled?` | `boolean` | `false` | Dims the whole group and blocks it. A single option can be disabled through its disabled field. |
| `aria-label?` | `string` | `""` | The group's name for screen readers, like Alignment. Needed when no visible label sits next to it. |
| `classNames?` | `Partial<Record<"item" \| "highlight" \| "icon" \| "label", string>>` |  | Extra classes for every item, the pressed highlight, the icon wrapper and the label wrapper. |
| `className?` | `string` |  | Extra classes for the group itself, merged last. |

## Parts

### ToggleGroupRoot (component)

The group without options, for custom layouts: the same props as ToggleGroup minus options, with ToggleGroupItem and ToggleGroupSeparator children. Props: ToggleGroupRootProps<T>.

### ToggleGroupItem (component)

One item; children is the label. Takes every button prop too. Props: { value: string; icon?: ReactNode; tooltip?: ReactNode; shortcut?: string; disabled?: boolean; children?: ReactNode }.

### ToggleGroupSeparator (component)

A hairline between clusters inside a group or between two groups in a toolbar; it runs across the group's direction unless you set orientation. Props: { orientation?: "horizontal" | "vertical" }.

### ToggleGroupOption (type)

One item of options: value, label, icon, disabled, tooltip (hover, focus and long press) and shortcut (a mod+k hint shown in the tooltip and set as aria-keyshortcuts; bind it with useShortcut).

### ToggleGroupValue (type)

The value type: a string for type="single", a string[] for type="multiple".

## Examples

- **Variants**: ghost for toolbars, outline for one bordered control, soft for a track with a solid pressed fill.
- **View switch**: Single, soft and full width, with icons and labels: the highlight slides as the view changes.
- **Quick filters**: type="multiple" with attached={false}: outline chips that wrap, each popping its own highlight in and out.
- **Vertical tool palette**: Parts with a separator; icon-only items show their name and shortcut in a tooltip on the right. With focus in the palette, the letter keys pick a tool.
- **Sizes and radii**: 32, 40 and 48px; the outline row runs from square corners to a pill.
- **Tones**: Brand for the billing period, success for the channels that are on.

## See also

- [Toggle](https://wingo-ui.com/components/toggle) ([Markdown](https://wingo-ui.com/components/toggle.md))
- [Segmented Control](https://wingo-ui.com/components/segmented-control) ([Markdown](https://wingo-ui.com/components/segmented-control.md))
- [Tooltip](https://wingo-ui.com/components/tooltip) ([Markdown](https://wingo-ui.com/components/tooltip.md))
- [Tabs](https://wingo-ui.com/components/tabs) ([Markdown](https://wingo-ui.com/components/tabs.md))

---

Canonical: https://wingo-ui.com/components/toggle-group
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
