# Toggle Group

An accessible React toggle group for Next.js: buttons you switch on and off independently, like Bold, Italic and Underline in a toolbar.

Source: https://ui.fewtone.com/components/toggle-group

## Installation

```bash
npx shadcn@latest add https://ui.fewtone.com/r/toggle-group.json
```

## Usage

```tsx
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

export function Formatting() {
  return (
    <ToggleGroup defaultValue={["bold"]} aria-label="Formatting">
      <ToggleGroupItem value="bold">Bold</ToggleGroupItem>
      <ToggleGroupItem value="italic">Italic</ToggleGroupItem>
      <ToggleGroupItem value="underline">Underline</ToggleGroupItem>
    </ToggleGroup>
  )
}
```

## API reference

### ToggleGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string[]` | – | The options switched on, when you control them. |
| `defaultValue` | `string[]` | – | The options switched on at first. |
| `onValueChange` | `(value: string[]) => void` | – | Called with every option that's on, after each change. |
| `variant` | `"default" \| "outline"` | `"default"` | Default: a muted track. Outline: a bordered track, with a bordered pill. The pill is dark in dark mode and light in light mode. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 32, 36 or 40px tall, matching Button. |
| `disabled` | `boolean` | `false` | Disables every option. |

### ToggleGroupItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | – | The value this option adds when it's on. |
| `disabled` | `boolean` | `false` | Skips the option. |

**When to use it:** Use ToggleGroup for options people switch on and off independently, like text formatting, weekdays or notification channels. Use Switcher for one choice from a few, and Checkbox for options in a form.
**Built on:** Radix Toggle Group.
**Accessibility pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/
**npm packages (installed by the CLI):** class-variance-authority@^0.7.1, motion@^14.0.0, radix-ui@^1.7.0.

## Examples

### Variants

Default has a muted track; Outline a bordered track and pill. The pill matches the theme, dark in dark mode and light in light mode. Each option you switch on fades its own pill in.

```tsx
"use client";

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react";
import { useId, useState } from "react";

type Size = "sm" | "md" | "lg";

const variants = [
  { variant: "default", label: "Default" },
  { variant: "outline", label: "Outline" },
] as const;

/*
 * Both variants side by side, named underneath, with the size in the top-right corner. An empty row at the
 * bottom, as tall as the setting's row, keeps them centred.
 */
export default function ToggleGroupVariants() {
  const id = useId();
  const [size, setSize] = useState<Size>("md");

  return (
    <div className="flex w-full flex-col gap-6 self-stretch">
      <div className="-mt-6 -mr-6 flex items-center justify-end gap-2">
        <label htmlFor={`${id}-size`} className="text-sm text-muted-foreground">
          Size
        </label>
        <Select value={size} onValueChange={(value) => setSize(value as Size)}>
          <SelectTrigger id={`${id}-size`} size="sm" className="w-28">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="sm">Small</SelectItem>
            <SelectItem value="md">Medium</SelectItem>
            <SelectItem value="lg">Large</SelectItem>
          </SelectContent>
        </Select>
      </div>

      <ul className="flex flex-1 flex-wrap items-center justify-center gap-x-10 gap-y-6">
        {variants.map(({ variant, label }) => (
          <li key={variant} className="flex flex-col items-center gap-2">
            <ToggleGroup
              defaultValue={["bold"]}
              variant={variant}
              size={size}
              aria-label={`Formatting, ${label.toLowerCase()} style`}
            >
              <ToggleGroupItem value="bold" aria-label="Bold">
                <BoldIcon />
              </ToggleGroupItem>
              <ToggleGroupItem value="italic" aria-label="Italic">
                <ItalicIcon />
              </ToggleGroupItem>
              <ToggleGroupItem value="underline" aria-label="Underline">
                <UnderlineIcon />
              </ToggleGroupItem>
            </ToggleGroup>
            <span className="text-xs text-muted-foreground">{label}</span>
          </li>
        ))}
      </ul>

      <div aria-hidden className="-mb-6 h-(--size-control-sm)" />
    </div>
  );
}
```

### Days

Text options work the same way, here picking the days to repeat on.

```tsx
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";

const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

export default function ToggleGroupDays() {
  return (
    <ToggleGroup defaultValue={["mon", "wed", "fri"]} aria-label="Repeat on">
      {days.map((day) => (
        <ToggleGroupItem key={day} value={day.toLowerCase()}>
          {day}
        </ToggleGroupItem>
      ))}
    </ToggleGroup>
  );
}
```

### A disabled option

Disabled options don't respond and the arrow keys skip them.

```tsx
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";

export default function ToggleGroupDisabled() {
  return (
    <ToggleGroup defaultValue={["email"]} variant="outline" aria-label="Notify by">
      <ToggleGroupItem value="email">Email</ToggleGroupItem>
      <ToggleGroupItem value="sms" disabled>
        SMS
      </ToggleGroupItem>
      <ToggleGroupItem value="push">Push</ToggleGroupItem>
    </ToggleGroup>
  );
}
```

## Related

- [Switcher](https://ui.fewtone.com/components/switcher.md): An accessible React segmented control for Next.js: one choice from a few options, with a pill that slides to the one you pick.
- [Button](https://ui.fewtone.com/components/button.md): An accessible React button for Next.js and Tailwind CSS with six variants, three sizes, icons and a soft tint on hover and press, built on a native button.
