> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# SwitchButton

> A labelled pill that switches one feature or mode on and off, for settings panels, filter bars and tool options.

```tsx theme={null}
import { SwitchButton } from "@fanvue/ui";
import type {
  SwitchButtonAiProps,
  SwitchButtonDefaultProps,
  SwitchButtonProps,
  SwitchButtonSize,
  SwitchButtonVariant,
} from "@fanvue/ui";
```

## Examples

## Default

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-switchbutton--default&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="200" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="SwitchButton — Default" />
</Frame>

```tsx theme={null}
import { SwitchButton } from "@fanvue/ui";

<SwitchButton label="CTA" size="40" variant="default" />
```

## Disabled

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-switchbutton--disabled&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="200" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="SwitchButton — Disabled" />
</Frame>

```tsx theme={null}
import { SwitchButton } from "@fanvue/ui";

<SwitchButton label="CTA" size="40" variant="default" disabled />
```

## Sizes

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-switchbutton--sizes&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="200" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="SwitchButton — Sizes" />
</Frame>

```tsx theme={null}
import { SwitchButton } from "@fanvue/ui";
import type { SwitchButtonSize } from "@fanvue/ui";

const SIZES: SwitchButtonSize[] = ["40", "32", "24"];

<div className="flex flex-col gap-6">
  {SIZES.map((size) => (
    <div key={size} className="flex items-center gap-4">
      <span className="typography-description-12px-regular w-10 text-content-secondary">
        {size}px
      </span>
      <SwitchButton size={size} label="CTA" />
      <SwitchButton size={size} label="CTA" defaultPressed />
      <SwitchButton size={size} variant="ai" label="CTA" />
      <SwitchButton size={size} variant="ai" label="CTA" defaultPressed />
    </div>
  ))}
</div>
```

## Icon slots

`showLeftIcon` and `showRightIcon` are independent booleans, each rendering the add glyph on that side. Both are available on the `default` variant only — the `ai` variant types them as `never`, since its sparkle is fixed and it has no trailing slot.

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-switchbutton--icon-slots&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="200" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="SwitchButton — Icon slots" />
</Frame>

```tsx theme={null}
import { SwitchButton } from "@fanvue/ui";
import type { SwitchButtonSize } from "@fanvue/ui";

const SIZES: SwitchButtonSize[] = ["40", "32", "24"];

<div className="flex flex-col gap-6">
  {SIZES.map((size) => (
    <div key={size} className="flex items-center gap-4">
      <span className="typography-description-12px-regular w-10 text-content-secondary">
        {size}px
      </span>
      <SwitchButton size={size} label="Neither" />
      <SwitchButton size={size} label="Left" showLeftIcon />
      <SwitchButton size={size} label="Right" showRightIcon />
      <SwitchButton size={size} label="Both" showLeftIcon showRightIcon />
    </div>
  ))}
</div>
```

## Matrix

Mirrors the `V2 Toggle` Figma frame: every size against every state, for both variants. The hover column is painted with the hover token directly, since a real `:hover` cannot be held open in a static snapshot — move the pointer over the inactive column to see the genuine transition.

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-switchbutton--matrix&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="200" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="SwitchButton — Matrix" />
</Frame>

```tsx theme={null}
import { SwitchButton } from "@fanvue/ui";
import type { SwitchButtonSize } from "@fanvue/ui";

const SIZES: SwitchButtonSize[] = ["40", "32", "24"];

const HOVER_CLASS = "bg-buttons-switch-hover";

<div className="flex flex-col gap-8">
  {(["default", "ai"] as const).map((variant) => (
    <div key={variant} className="flex flex-col gap-4">
      <span className="typography-body-small-14px-semibold text-content-secondary">
        {variant === "ai" ? "AI" : "Default"}
      </span>
      <div className="grid grid-cols-[3rem_repeat(4,minmax(6rem,auto))] items-center justify-items-start gap-x-4 gap-y-3">
        <span />
        {["Inactive", "Hover", "Active", "Disabled"].map((state) => (
          <span
            key={state}
            className="typography-description-12px-regular text-content-secondary"
          >
            {state}
          </span>
        ))}
        {SIZES.map((size) => (
          <div key={size} className="col-span-5 grid grid-cols-subgrid items-center">
            <span className="typography-description-12px-regular text-content-secondary">
              {size}px
            </span>
            <SwitchButton variant={variant} size={size} label="CTA" />
            <SwitchButton
              variant={variant}
              size={size}
              label="CTA"
              className={HOVER_CLASS}
              tabIndex={-1}
            />
            <SwitchButton variant={variant} size={size} label="CTA" defaultPressed />
            <SwitchButton variant={variant} size={size} label="CTA" disabled />
          </div>
        ))}
      </div>
    </div>
  ))}
</div>
```

## Props

## SwitchButton

A labelled pill that switches one feature or mode on and off, for settings panels, filter bars and tool options. Unlike `Switch` it names the thing it controls, which suits contexts where the action needs spelling out.

Rendered as a `<button>` carrying `aria-pressed` — the ARIA toggle-button pattern — so it flips on click and on Enter or Space, and exposes `data-state="on" | "off"` for styling. Supports controlled (`pressed`) and uncontrolled (`defaultPressed`) usage. The visible `label` is the accessible name, so no `aria-label` is needed.

For two or three mutually exclusive options, reach for `SegmentedControl` instead: this button is independently on or off, not a choice between options.

<ParamField path="label" type="string" required>
  Visible text label, which is also the accessible name.
</ParamField>

<ParamField path="defaultPressed" type="boolean" default="false">
  Initial on/off state for uncontrolled usage.
</ParamField>

<ParamField path="leftIcon" type="ReactNode" default="<AddIcon size={16} />">
  Icon rendered before the label when `showLeftIcon` is set. Unavailable on `"ai"` — the leading sparkle is fixed.
</ParamField>

<ParamField path="onPressedChange" type="((pressed: boolean) => void)">
  Fired with the next state each time the button flips.
</ParamField>

<ParamField path="pressed" type="boolean">
  On/off state for controlled usage. Pair with `onPressedChange`.
</ParamField>

<ParamField path="rightIcon" type="ReactNode" default="<AddIcon size={16} />">
  Icon rendered after the label when `showRightIcon` is set. Unavailable on `"ai"` — this variant has no trailing icon slot.
</ParamField>

<ParamField path="showLeftIcon" type="boolean" default="false">
  Show an icon before the label. Unavailable on `"ai"` — the leading sparkle is always shown and cannot be swapped.
</ParamField>

<ParamField path="showRightIcon" type="boolean" default="false">
  Show an icon after the label. Unavailable on `"ai"` — this variant has no trailing icon slot.
</ParamField>

<ParamField path="size" type="&#x22;24&#x22; | &#x22;32&#x22; | &#x22;40&#x22;" default="32">
  Height of the button in pixels.
</ParamField>

<ParamField path="type" type="&#x22;button&#x22; | &#x22;submit&#x22; | &#x22;reset&#x22;" default="button">
  Native button behaviour.
</ParamField>

<ParamField path="variant" type="&#x22;default&#x22; | &#x22;ai&#x22;" default="default">
  Visual treatment.
</ParamField>

## Exported types

Also exported from `@fanvue/ui`: `SwitchButtonAiProps`, `SwitchButtonDefaultProps`, `SwitchButtonProps`, `SwitchButtonSize`, `SwitchButtonVariant`.

***

**Setup:** [Installation](/docs/ui/installation) · [Theming](/docs/ui/theming)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.