# Toggle Group

A row of toggles that share one value.

```tsx
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'

export default function ToggleGroupHero() {
  return (
    <ToggleGroup type="single" variant="outline" defaultValue="30" aria-label="Period of the report">
      <ToggleGroupItem value="7">7 days</ToggleGroupItem>
      <ToggleGroupItem value="30">30 days</ToggleGroupItem>
      <ToggleGroupItem value="90">90 days</ToggleGroupItem>
    </ToggleGroup>
  )
}
```

## Usage guidelines

- **A compact choice.** Use a toggle group to change the view or the format of the same content.
- **Not for panels.** To show one panel of a set, use [Tabs](/docs/components/tabs).
- **Not for a long list.** For many options, use [Select](/docs/components/select) or [Radio Group](/docs/components/radio-group).
- **One toggle alone.** For one option with two states, use [Toggle](/docs/components/toggle).

## Anatomy

Import the two parts. The `type` prop of the group and the `value` prop of each item are required.

```tsx title="Anatomy"

<ToggleGroup type="single">
  <ToggleGroupItem />
</ToggleGroup>
```

| Part | Role |
| --- | --- |
| `ToggleGroup` | Holds the value. It gives its `variant` and its `size` to each item. |
| `ToggleGroupItem` | One option. Its `value` identifies it. |

## Examples

### Single and multiple

The `type` prop sets how many items the user can press.

| Type | Value | Use |
| --- | --- | --- |
| `single` | A string | One option of a set: a view, a period, an alignment. |
| `multiple` | An array of strings | Options that do not depend on each other. |

The group holds its value by default. Use `defaultValue` to set it at the start. To control the value, pass `value` and `onValueChange`.

```tsx
import * as React from 'react'
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'
import { Bold, Italic, Underline } from 'lucide-react'

export default function ToggleGroupMultiple() {
  const [formats, setFormats] = React.useState(['bold'])

  return (
    <div className="flex flex-col items-center gap-3">
      <ToggleGroup type="multiple" variant="outline" aria-label="Text format" value={formats} onValueChange={setFormats}>
        <ToggleGroupItem value="bold" aria-label="Bold">
          <Bold />
        </ToggleGroupItem>
        <ToggleGroupItem value="italic" aria-label="Italic">
          <Italic />
        </ToggleGroupItem>
        <ToggleGroupItem value="underline" aria-label="Underline">
          <Underline />
        </ToggleGroupItem>
      </ToggleGroup>
      <p className="font-mono text-xs text-foreground-lighter">value: {JSON.stringify(formats)}</p>
    </div>
  )
}
```

### One item always pressed

With `type="single"`, a click on the pressed item sends an empty string. If one item must stay pressed, ignore the empty value in `onValueChange`.

```tsx
import * as React from 'react'
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'
import { LayoutGrid, List } from 'lucide-react'

const PROJECTS = ['Customer portal', 'Marketing site', 'Mobile app', 'Billing portal']

export default function ToggleGroupRequired() {
  const [view, setView] = React.useState('grid')

  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <ToggleGroup
        type="single"
        variant="outline"
        aria-label="View"
        value={view}
        // A click on the pressed item sends "". Ignore it to keep one item pressed.
        onValueChange={(next) => next && setView(next)}
      >
        <ToggleGroupItem value="grid" aria-label="Grid view">
          <LayoutGrid />
        </ToggleGroupItem>
        <ToggleGroupItem value="list" aria-label="List view">
          <List />
        </ToggleGroupItem>
      </ToggleGroup>
      <ul className={view === 'grid' ? 'grid grid-cols-2 gap-2' : 'flex flex-col gap-2'}>
        {PROJECTS.map((project) => (
          <li key={project} className="rounded-md border bg-surface-100 px-3 py-2 text-[13px] text-foreground">
            {project}
          </li>
        ))}
      </ul>
    </div>
  )
}
```

### Variants

The `variant` prop of the group sets the look of each item.

```tsx
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'

export default function ToggleGroupVariants() {
  return (
    <>
      <ToggleGroup type="single" defaultValue="month" aria-label="Billing period, default variant">
        <ToggleGroupItem value="month">Monthly</ToggleGroupItem>
        <ToggleGroupItem value="year">Yearly</ToggleGroupItem>
      </ToggleGroup>
      <ToggleGroup type="single" variant="outline" defaultValue="month" aria-label="Billing period, outline variant">
        <ToggleGroupItem value="month">Monthly</ToggleGroupItem>
        <ToggleGroupItem value="year">Yearly</ToggleGroupItem>
      </ToggleGroup>
    </>
  )
}
```

| Variant | Look |
| --- | --- |
| `default` | Items with no border. |
| `outline` | One control with a border. |

### Sizes

The `size` prop of the group sets the height of each item.

```tsx
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'

const SIZES = ['tiny', 'sm', 'md'] as const

export default function ToggleGroupSizes() {
  return (
    <>
      {SIZES.map((size) => (
        <ToggleGroup key={size} type="single" variant="outline" size={size} defaultValue="grid" aria-label={`View, size ${size}`}>
          <ToggleGroupItem value="grid">Grid</ToggleGroupItem>
          <ToggleGroupItem value="list">List</ToggleGroupItem>
        </ToggleGroup>
      ))}
    </>
  )
}
```

| Size | Height |
| --- | --- |
| `tiny` | 26px |
| `sm` | 30px (default) |
| `md` | 34px |

### Spacing

By default, the items make one control. Pass `spacing` to put a gap between them. One unit is 4px.

```tsx
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'

export default function ToggleGroupSpacing() {
  return (
    <ToggleGroup type="multiple" variant="outline" spacing={2} defaultValue={['email']} aria-label="Notification channels">
      <ToggleGroupItem value="email">Email</ToggleGroupItem>
      <ToggleGroupItem value="sms">SMS</ToggleGroupItem>
      <ToggleGroupItem value="push">Push</ToggleGroupItem>
    </ToggleGroup>
  )
}
```

### Disabled state

Set `disabled` on one item or on the group.

```tsx
import { ToggleGroup, ToggleGroupItem } from 'ferry-ui'

export default function ToggleGroupDisabled() {
  return (
    <>
      <ToggleGroup type="single" variant="outline" defaultValue="month" aria-label="Billing period">
        <ToggleGroupItem value="month">Monthly</ToggleGroupItem>
        <ToggleGroupItem value="year">Yearly</ToggleGroupItem>
        <ToggleGroupItem value="lifetime" disabled>
          Lifetime
        </ToggleGroupItem>
      </ToggleGroup>
      <ToggleGroup type="single" variant="outline" defaultValue="month" disabled aria-label="Billing period, disabled">
        <ToggleGroupItem value="month">Monthly</ToggleGroupItem>
        <ToggleGroupItem value="year">Yearly</ToggleGroupItem>
      </ToggleGroup>
    </>
  )
}
```

### Visible label

Give the group an `aria-label`. For a label that the user sees, put the group in a [Field](/docs/components/field) with `labelAs="span"`.

```tsx
import { Field, ToggleGroup, ToggleGroupItem } from 'ferry-ui'
import { Monitor, Moon, Sun } from 'lucide-react'

export default function ToggleGroupField() {
  return (
    <Field label="Theme" labelAs="span" hint="The system option follows the theme of the device.">
      <ToggleGroup type="single" variant="outline" defaultValue="system">
        <ToggleGroupItem value="light">
          <Sun /> Light
        </ToggleGroupItem>
        <ToggleGroupItem value="dark">
          <Moon /> Dark
        </ToggleGroupItem>
        <ToggleGroupItem value="system">
          <Monitor /> System
        </ToggleGroupItem>
      </ToggleGroup>
    </Field>
  )
}
```

## Accessibility

- The arrow keys move the focus between the items. <Kbd>Space</Kbd> or <Kbd>Enter</Kbd> presses the item that has the focus.
- An item with only an icon must have its own `aria-label`. For a tooltip, wrap the item in [`Hint`](/docs/components/tooltip).

## API reference

Each part also accepts the props of its Radix UI primitive and the attributes of its element.

### ToggleGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type` (required) | `"multiple" \| "single"` |  |  |
| `variant` | `"default" \| "outline"` |  | `default` = transparent items; `outline` = bordered segmented control on surface-100. Applied to every item. |
| `size` | `"default" \| "tiny" \| "sm" \| "md" \| "lg"` |  | Item height, on the `Button` scale: `tiny` 26px, `sm` 30px (default), `md` 34px. Applied to every item. `default` and `lg` are deprecated aliases of `sm` and `md`. |
| `spacing` | `number` | `0` | Gap between items in Tailwind spacing units (1 = 4px). `0` (default) joins the items into a single segmented control. |
| `value` | `string \| string[]` |  | The controlled stateful value of the item that is pressed. The controlled stateful value of the items that are pressed. |
| `defaultValue` | `string \| string[]` |  | The value of the item that is pressed when initially rendered. Use `defaultValue` if you do not need to control the state of a toggle group. The value of the items that are pressed when initially rendered. Use `defaultValue` if you do not need to control the state of a toggle group. |
| `onValueChange` | `((value: string) => void) \| ((value: string[]) => void)` |  | The callback that fires when the value of the toggle group changes. The callback that fires when the state of the toggle group changes. |
| `disabled` | `boolean` | `false` | Whether the group is disabled from user interaction. |
| `rovingFocus` | `boolean` | `true` | Whether the group should maintain roving focus of its buttons. |
| `loop` | `boolean` |  |  |
| `orientation` | `"horizontal" \| "vertical"` |  |  |
| `dir` | `"ltr" \| "rtl"` |  |  |
| `asChild` | `boolean` |  |  |

### ToggleGroupItem

`value` is a required string.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `string` |  | A string value for the toggle group item. All items within a toggle group should use a unique value. |
| `variant` | `"default" \| "outline"` |  | `default` = transparent items; `outline` = bordered segmented control on surface-100. Applied to every item. |
| `size` | `"default" \| "tiny" \| "sm" \| "md" \| "lg"` |  | Item height, on the `Button` scale: `tiny` 26px, `sm` 30px (default), `md` 34px. Applied to every item. `default` and `lg` are deprecated aliases of `sm` and `md`. |
| `asChild` | `boolean` |  |  |
