

<Preview name="BasicToggleGroupExample" />

## Overview [#overview]

The `ToggleGroup` component manages a collection of toggle buttons that can work independently or together. Built on Base UI primitives, it supports multiple selection modes, variants (including an animated segmented control style), and customizable sizes.

## Usage [#usage]

```tsx
import { ToggleGroup, ToggleGroupItem } from "@tilt-legal/cubitt-components/primitives";
```

```tsx
<ToggleGroup multiple={true} defaultValue={["bold"]}>
  <ToggleGroupItem value="bold" aria-label="Toggle bold">
    <TextBold />
  </ToggleGroupItem>
  <ToggleGroupItem value="italic" aria-label="Toggle italic">
    <TextItalic />
  </ToggleGroupItem>
</ToggleGroup>
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Using Cubitt's URL-state hooks, you can sync the toggle group selection with the URL by providing a `paramName`. This is useful for shareable links, back/forward navigation, and maintaining state across page refreshes.

    ```tsx
    // Single select - synced with ?view=week
    <ToggleGroup
      groupVariant="segmented"
      paramName="view"
      defaultValue="week"
      paramClearOnDefault={true}
    >
      <ToggleGroupItem value="day">Day</ToggleGroupItem>
      <ToggleGroupItem value="week">Week</ToggleGroupItem>
      <ToggleGroupItem value="month">Month</ToggleGroupItem>
    </ToggleGroup>

    // Multi-select - synced with ?formats=bold,italic
    <ToggleGroup
      multiple
      paramName="formats"
      defaultValue={[]}
      paramClearOnDefault={true}
    >
      <ToggleGroupItem value="bold">Bold</ToggleGroupItem>
      <ToggleGroupItem value="italic">Italic</ToggleGroupItem>
    </ToggleGroup>

    // Advanced options:
    <ToggleGroup
      paramName="view"
      paramClearOnDefault={true}    // Remove param when value equals default
      paramDebounce={300}           // Debounce URL updates
      onUrlValueChange={(value) => console.log("Selected:", value)}
    >
      {/* ... */}
    </ToggleGroup>
    ```
  </Accordion>

  <Accordion title="Polymorphic Rendering">
    `ToggleGroupItem` forwards Base UI's polymorphic `render` support. When an item renders as something other than a native `<button>` such as a router link, pass `nativeButton={false}` so keyboard and pressed-state behavior stay correct.

    ```tsx
    import { Link } from "@tanstack/react-router";
    import {
      ToggleGroup,
      ToggleGroupItem,
    } from "@tilt-legal/cubitt-components/primitives";

    <ToggleGroup defaultValue="week" groupVariant="segmented">
      <ToggleGroupItem value="day">Day</ToggleGroupItem>
      <ToggleGroupItem
        value="week"
        nativeButton={false}
        render={<Link href="/week" />}
      >
        Week
      </ToggleGroupItem>
    </ToggleGroup>
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### Segmented Variant [#segmented-variant]

Toggle group with animated sliding indicator for single selection.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="ToggleGroupSegmentedExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { ToggleGroup, ToggleGroupItem } from "@tilt-legal/cubitt-components/primitives";
    import { ToggleGroupItem } from "@tilt-legal/cubitt-components/primitives";
    import {
      TextAlignLeft,
      TextAlignCenter,
      TextAlignRight,
      } from "@tilt-legal/cubitt-icons/ui/outline";

    export default function Example() {
      return (
        <ToggleGroup groupVariant="segmented" multiple={false} defaultValue="center">
          <ToggleGroupItem mode="icon" value="left" aria-label="Align left">
            <TextAlignLeft />
          </ToggleGroupItem>
          <ToggleGroupItem mode="icon" value="center" aria-label="Align center">
            <TextAlignCenter />
          </ToggleGroupItem>
          <ToggleGroupItem mode="icon" value="right" aria-label="Align right">
            <TextAlignRight />
          </ToggleGroupItem>
        </ToggleGroup>
      );
    }
    ```
  </Tab>
</Tabs>

### Segmented Sizes [#segmented-sizes]

Segmented toggle groups in different sizes with animated indicator.

<Tabs
  items="[&#x22;Preview&#x22;,
    &#x22;Code&#x22;]"
>
  <Tab value="Preview">
    <Preview name="ToggleGroupSegmentedSizesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { ToggleGroup } from "@tilt-legal/cubitt-components/primitives";

    import { ToggleGroupItem } from "@tilt-legal/cubitt-components/primitives";
    import {
      TextAlignLeft,
      TextAlignCenter,
      TextAlignRight } from "@tilt-legal/cubitt-icons/ui/outline";

    export default function Example() {
      return (
        <div className="flex flex-col items-start gap-6">
          <div className="space-y-2">
            <p className="text-sm font-medium">Small</p>
            <div className="flex items-center gap-4">
              <ToggleGroup groupVariant="segmented" size="sm" defaultValue="week">
                <ToggleGroupItem value="day">Day</ToggleGroupItem>
                <ToggleGroupItem value="week">Week</ToggleGroupItem>
                <ToggleGroupItem value="month">Month</ToggleGroupItem>
                <ToggleGroupItem value="year">Year</ToggleGroupItem>
              </ToggleGroup>
              <ToggleGroup groupVariant="segmented" size="sm" defaultValue="center">
                <ToggleGroupItem mode="icon" value="left" aria-label="Align left">
                  <TextAlignLeft />
                </ToggleGroupItem>
                <ToggleGroupItem mode="icon" value="center" aria-label="Align center">
                  <TextAlignCenter />
                </ToggleGroupItem>
                <ToggleGroupItem mode="icon" value="right" aria-label="Align right">
                  <TextAlignRight />
                </ToggleGroupItem>
              </ToggleGroup>
            </div>
          </div>
          <div className="space-y-2">
            <p className="text-sm font-medium">Medium (Default)</p>
            <div className="flex items-center gap-4">
              <ToggleGroup groupVariant="segmented" size="md" defaultValue="week">
                <ToggleGroupItem value="day">Day</ToggleGroupItem>
                <ToggleGroupItem value="week">Week</ToggleGroupItem>
                <ToggleGroupItem value="month">Month</ToggleGroupItem>
                <ToggleGroupItem value="year">Year</ToggleGroupItem>
              </ToggleGroup>
              <ToggleGroup groupVariant="segmented" size="md" defaultValue="center">
                <ToggleGroupItem mode="icon" value="left" aria-label="Align left">
                  <TextAlignLeft />
                </ToggleGroupItem>
                <ToggleGroupItem mode="icon" value="center" aria-label="Align center">
                  <TextAlignCenter />
                </ToggleGroupItem>
                <ToggleGroupItem mode="icon" value="right" aria-label="Align right">
                  <TextAlignRight />
                </ToggleGroupItem>
              </ToggleGroup>
            </div>
          </div>
          <div className="space-y-2">
            <p className="text-sm font-medium">Large</p>
            <div className="flex items-center gap-4">
              <ToggleGroup groupVariant="segmented" size="lg" defaultValue="week">
                <ToggleGroupItem value="day">Day</ToggleGroupItem>
                <ToggleGroupItem value="week">Week</ToggleGroupItem>
                <ToggleGroupItem value="month">Month</ToggleGroupItem>
                <ToggleGroupItem value="year">Year</ToggleGroupItem>
              </ToggleGroup>
              <ToggleGroup groupVariant="segmented" size="lg" defaultValue="center">
                <ToggleGroupItem mode="icon" value="left" aria-label="Align left">
                  <TextAlignLeft />
                </ToggleGroupItem>
                <ToggleGroupItem mode="icon" value="center" aria-label="Align center">
                  <TextAlignCenter />
                </ToggleGroupItem>
                <ToggleGroupItem mode="icon" value="right" aria-label="Align right">
                  <TextAlignRight />
                </ToggleGroupItem>
              </ToggleGroup>
            </div>
          </div>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Segmented With Separator [#segmented-with-separator]

Use `ToggleGroupSeparator` inside the segmented track to split related options. The separator fades out whenever the active pill touches it, then becomes visible again once selection moves away.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="ToggleGroupSegmentedSeparatorExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      ToggleGroup,
      ToggleGroupItem,
      ToggleGroupSeparator,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <ToggleGroup groupVariant="segmented" defaultValue="day">
          <ToggleGroupItem value="day">Day</ToggleGroupItem>
          <ToggleGroupItem value="week">Week</ToggleGroupItem>
          <ToggleGroupSeparator />
          <ToggleGroupItem value="month">Month</ToggleGroupItem>
          <ToggleGroupItem value="year">Year</ToggleGroupItem>
        </ToggleGroup>
      );
    }
    ```
  </Tab>
</Tabs>

### Outline Variant [#outline-variant]

Toggle group with outlined buttons.

<Tabs
  items="[&#x22;Preview&#x22;,
    &#x22;Code&#x22;]"
>
  <Tab value="Preview">
    <Preview name="ToggleGroupOutlineExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { ToggleGroup } from "@tilt-legal/cubitt-components/primitives";
    import { ToggleGroupItem } from "@tilt-legal/cubitt-components/primitives";
    import {
      TextBold,
      TextItalic,
      TextUnderline } from "@tilt-legal/cubitt-icons/ui/outline";

    export default function Example() {
      return (
        <ToggleGroup groupVariant="outline" multiple={true} defaultValue={["bold",
      "italic"]}>
          <ToggleGroupItem value="bold" aria-label="Toggle bold">
            <TextBold />
          </ToggleGroupItem>
          <ToggleGroupItem value="italic" aria-label="Toggle italic">
            <TextItalic />
          </ToggleGroupItem>
          <ToggleGroupItem value="underline" aria-label="Toggle underline">
            <TextUnderline />
          </ToggleGroupItem>
        </ToggleGroup>
      );
    }
    ```
  </Tab>
</Tabs>

### Outline Sizes [#outline-sizes]

Toggle groups in different sizes.

<Tabs
  items="[&#x22;Preview&#x22;,
    &#x22;Code&#x22;]"
>
  <Tab value="Preview">
    <Preview name="ToggleGroupSizesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { ToggleGroup } from "@tilt-legal/cubitt-components/primitives";
    import { ToggleGroupItem } from "@tilt-legal/cubitt-components/primitives";
    import {
      TextBold,
      TextItalic,
      TextUnderline } from "@tilt-legal/cubitt-icons/ui/outline";

    export default function Example() {
      return (
        <div className="flex flex-col items-start gap-6">
          <div className="space-y-2">
            <p className="text-sm font-medium">Small</p>
            <ToggleGroup groupVariant="outline" size="sm" multiple={true} defaultValue={["bold"]}>
              <ToggleGroupItem value="bold" aria-label="Toggle bold">
                <TextBold />
              </ToggleGroupItem>
              <ToggleGroupItem value="italic" aria-label="Toggle italic">
                <TextItalic />
              </ToggleGroupItem>
              <ToggleGroupItem value="underline" aria-label="Toggle underline">
                <TextUnderline />
              </ToggleGroupItem>
            </ToggleGroup>
          </div>
          <div className="space-y-2">
            <p className="text-sm font-medium">Medium (Default)</p>
            <ToggleGroup groupVariant="outline" size="md" multiple={true} defaultValue={["bold"]}>
              <ToggleGroupItem value="bold" aria-label="Toggle bold">
                <TextBold />
              </ToggleGroupItem>
              <ToggleGroupItem value="italic" aria-label="Toggle italic">
                <TextItalic />
              </ToggleGroupItem>
              <ToggleGroupItem value="underline" aria-label="Toggle underline">
                <TextUnderline />
              </ToggleGroupItem>
            </ToggleGroup>
          </div>
          <div className="space-y-2">
            <p className="text-sm font-medium">Large</p>
            <ToggleGroup groupVariant="outline" size="lg" multiple={true} defaultValue={["bold"]}>
              <ToggleGroupItem value="bold" aria-label="Toggle bold">
                <TextBold />
              </ToggleGroupItem>
              <ToggleGroupItem value="italic" aria-label="Toggle italic">
                <TextItalic />
              </ToggleGroupItem>
              <ToggleGroupItem value="underline" aria-label="Toggle underline">
                <TextUnderline />
              </ToggleGroupItem>
            </ToggleGroup>
          </div>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### URL State [#url-state]

Sync toggle group selection with the URL. Try selecting a value and refreshing the page.

<Tabs
  items="[&#x22;Preview&#x22;,
    &#x22;Code&#x22;]"
>
  <Tab value="Preview">
    <Preview name="ToggleGroupUrlStateExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { ToggleGroup } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <div className="space-y-4">
          <ToggleGroup
            groupVariant="segmented"
            paramName="view"
            defaultValue="week"
            paramClearOnDefault={true}
          >
            <ToggleGroupItem value="day">Day</ToggleGroupItem>
            <ToggleGroupItem value="week">Week</ToggleGroupItem>
            <ToggleGroupItem value="month">Month</ToggleGroupItem>
            <ToggleGroupItem value="year">Year</ToggleGroupItem>
          </ToggleGroup>
          <p className="text-sm text-muted-foreground">
            Check the URL - the selected value is synced with the <code>?view=</code>{" "}
            parameter.
          </p>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Polymorphic Rendering [#polymorphic-rendering]

Render segmented toggle items through a custom link component while preserving pressed state.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="ToggleGroupSegmentedPolymorphicExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Link } from "@tanstack/react-router";
    import {
      ToggleGroup,
      ToggleGroupItem,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <ToggleGroup defaultValue="week" groupVariant="segmented" size="lg">
          <ToggleGroupItem value="day">Day</ToggleGroupItem>
          <ToggleGroupItem
            value="week"
            nativeButton={false}
            render={<Link href="/week" />}
          >
            Week
          </ToggleGroupItem>
          <ToggleGroupItem
            value="month"
            nativeButton={false}
            render={<Link href="/month" />}
          >
            Month
          </ToggleGroupItem>
          <ToggleGroupItem value="year">Year</ToggleGroupItem>
        </ToggleGroup>
      );
    }
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### ToggleGroup [#togglegroup]

The root container component that manages the toggle button group.

| Prop            | Type                                    | Default        | Description                                                                                                         |
| --------------- | --------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------- |
| `value`         | `string \| string[]`                    | —              | The controlled value(s) of the pressed item(s). String for single, array for multiple.                              |
| `defaultValue`  | `string \| string[]`                    | —              | The default value(s) when uncontrolled.                                                                             |
| `onValueChange` | `(value: string \| string[]) => void`   | —              | Callback fired when the value changes. Returns string for single, array for multiple.                               |
| `multiple`      | `boolean`                               | —              | When `true`, multiple items can be pressed. When `false`, only one. Auto-detected from value type if not specified. |
| `groupVariant`  | `"default" \| "outline" \| "segmented"` | `"default"`    | Visual style variant. Segmented shows animated indicator for single selection.                                      |
| `variant`       | `"default" \| "outline"`                | `"default"`    | Toggle button variant (passed to items).                                                                            |
| `size`          | `"sm" \| "md" \| "lg"`                  | `"md"`         | Size of the toggle buttons.                                                                                         |
| `disabled`      | `boolean`                               | `false`        | Whether all toggle buttons are disabled.                                                                            |
| `loop`          | `boolean`                               | `true`         | Whether keyboard focus loops back to the first item.                                                                |
| `orientation`   | `"horizontal" \| "vertical"`            | `"horizontal"` | Layout orientation of the toggle group.                                                                             |
| `className`     | `string`                                | —              | Additional CSS classes for the toggle group container.                                                              |

#### URL State Props [#url-state-props]

When provided with a `paramName`, the toggle group will sync its selection with URL parameters via TanStack Router search params.

| Prop                  | Type                                          | Default | Description                                                                  |
| --------------------- | --------------------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `paramName`           | `string`                                      | —       | URL parameter name for syncing state with URL. Enables URL state management. |
| `onUrlValueChange`    | `(value: string \| string[] \| null) => void` | —       | Callback when URL parameter value changes.                                   |
| `paramClearOnDefault` | `boolean`                                     | `true`  | Remove URL param when value equals default.                                  |
| `paramThrottle`       | `number`                                      | —       | Throttle URL updates in milliseconds.                                        |
| `paramDebounce`       | `number`                                      | —       | Debounce URL updates in milliseconds.                                        |

### ToggleGroupItem [#togglegroupitem]

Individual toggle button within a ToggleGroup.

| Prop           | Type                       | Default | Description                                                                     |
| -------------- | -------------------------- | ------- | ------------------------------------------------------------------------------- |
| `value`        | `string`                   | —       | Unique value for this toggle button (required).                                 |
| `mode`         | `"icon"`                   | —       | Square sizing for icon-only content. Renders as a circle in segmented variant.  |
| `disabled`     | `boolean`                  | `false` | Whether this specific toggle button is disabled.                                |
| `variant`      | `"default" \| "outline"`   | —       | Override the group's variant for this item.                                     |
| `size`         | `"sm" \| "md" \| "lg"`     | —       | Override the group's size for this item.                                        |
| `render`       | `ReactElement \| function` | —       | Render the item as a custom element or component while preserving toggle state. |
| `nativeButton` | `boolean`                  | `true`  | Set to `false` when `render` outputs a non-button element like `Link` or `a`.   |
| `className`    | `string`                   | —       | Additional CSS classes for the toggle button.                                   |
| `children`     | `React.ReactNode`          | —       | Content of the toggle button (icons, text, etc.).                               |

### Notes [#notes]

* **Selection modes**: Set `multiple={true}` for multi-select or `multiple={false}` for single-select. If omitted, auto-detects from value type (array = multiple, string = single)
* **Multiple selection** (`multiple={true}`): Users can toggle multiple items on/off independently
* **Single selection** (`multiple={false}`): Only one item can be selected at a time
* The `segmented` variant with single selection (`multiple={false}`) provides an animated sliding indicator
* `onValueChange` returns the appropriate format: string for single selection, array for multiple selection
* For icon-only buttons, always provide `aria-label` for accessibility
* The component uses Motion for animations in segmented variant, respecting `prefers-reduced-motion`
