> ## 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.

# SegmentedControl

> A compact selector for choosing between two or three mutually exclusive options where the choice affects the content immediately below it.

```tsx theme={null}
import { SegmentedControl } from "@fanvue/ui";
import type {
  SegmentedControlAppearance,
  SegmentedControlOption,
  SegmentedControlProps,
  SegmentedControlSize,
  SegmentedControlVariant,
} from "@fanvue/ui";
```

## Examples

## Brand

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

```tsx theme={null}
import { AIIcon, HomeIcon, SegmentedControl } from "@fanvue/ui";

const brandOptions = [
  { label: "Home", value: "home", icon: <HomeIcon size={16} aria-hidden="true" /> },
  { label: "Agent", value: "agent", icon: <AIIcon size={16} aria-hidden="true" />, ai: true },
];

<SegmentedControl
  appearance="brand"
  options={brandOptions}
  defaultValue="agent"
  aria-label="Navigation mode"
/>
```

## Disabled

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

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

const twoOptions = [
  { label: "Net", value: "net" },
  { label: "Gross", value: "gross" },
];

<SegmentedControl options={twoOptions} disabled aria-label="Amount type" />
```

## Controlled

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

```tsx theme={null}
import { useState } from "react";
import { SegmentedControl } from "@fanvue/ui";

const threeOptions = [
  { label: "Net", value: "net" },
  { label: "Gross", value: "gross" },
  { label: "Total", value: "total" },
];

function Example() {
  const [value, setValue] = useState("net");
  return (
    <div className="flex flex-col items-center gap-3">
      <SegmentedControl
        options={threeOptions}
        value={value}
        onChange={setValue}
        aria-label="Amount type"
      />
      <span className="typography-body-small-14px-regular text-content-secondary">
        Selected: {value}
      </span>
    </div>
  );
}
```

## All Variants

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

```tsx theme={null}
import { SegmentedControl } from "@fanvue/ui";
import type { SegmentedControlSize, SegmentedControlVariant } from "@fanvue/ui";

const twoOptions = [
  { label: "Net", value: "net" },
  { label: "Gross", value: "gross" },
];

const threeOptions = [
  { label: "Net", value: "net" },
  { label: "Gross", value: "gross" },
  { label: "Total", value: "total" },
];

function Example() {
  const sizes: SegmentedControlSize[] = ["32", "40", "48"];
  const variants: SegmentedControlVariant[] = ["hug", "fill"];
  return (
    <div className="flex flex-col items-start gap-8">
      {variants.map((variant) => (
        <div key={variant} className="flex flex-col items-start gap-4">
          <span className="typography-body-default-16px-semibold text-content-primary capitalize">
            {variant}
          </span>
          {sizes.map((size) => (
            <div
              key={size}
              className="flex flex-col items-start gap-3"
              style={{ width: variant === "fill" ? 560 : "auto" }}
            >
              <span className="typography-description-12px-regular text-content-secondary">
                {size}px
              </span>
              <SegmentedControl
                size={size}
                variant={variant}
                options={twoOptions}
                aria-label={`${variant} ${size}px two options`}
              />
              <SegmentedControl
                size={size}
                variant={variant}
                options={threeOptions}
                aria-label={`${variant} ${size}px three options`}
              />
            </div>
          ))}
        </div>
      ))}
    </div>
  );
}
```

## Collapsible

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

```tsx theme={null}
import { useState } from "react";
import { GridViewIcon, ListViewIcon, SegmentedControl } from "@fanvue/ui";

const iconOnlyOptions = [
  { label: "List view", value: "list", icon: <ListViewIcon size={16} aria-hidden="true" /> },
  { label: "Grid view", value: "grid", icon: <GridViewIcon size={16} aria-hidden="true" /> },
];

function Example() {
  const [view, setView] = useState("list");
  return (
    <div className="flex flex-col items-start gap-3">
      <span className="typography-body-small-14px-regular text-content-secondary">
        Drag the right edge to shrink the container — the control collapses to the selected icon
        and cycles on click.
      </span>
      <div
        className="resize-x overflow-auto rounded-lg border border-border-primary p-4"
        style={{ width: 260, minWidth: 72, maxWidth: 420 }}
      >
        <SegmentedControl
          appearance="plain"
          collapsible
          options={iconOnlyOptions}
          aria-label="View"
          value={view}
          onChange={setView}
        />
      </div>
      <span className="typography-body-small-14px-regular text-content-secondary">
        Selected: {view}
      </span>
    </div>
  );
}
```

## Props

## SegmentedControl

A compact selector for choosing between two or three mutually exclusive options where the choice affects the content immediately below it. Use instead of tabs when the options are more like settings or filters than navigation, such as toggling a list/grid view or a monthly/annual price.

Rendered as a `radiogroup` with roving-tabindex keyboard navigation. Supports both controlled and uncontrolled usage. With `collapsible`, the icon-bearing `plain`/`brand` appearances shrink to a single cycling icon toggle when space is tight (rendered as a `group` containing one button).

<ParamField path="options" type="SegmentedControlOption[]" required>
  The selectable segments. Designed for two or three mutually exclusive options.
</ParamField>

<ParamField path="appearance" type="&#x22;brand&#x22; | &#x22;pill&#x22; | &#x22;plain&#x22;" default="pill">
  Visual style of the control.
</ParamField>

<ParamField path="collapsedIcon" type="ReactNode">
  Single glyph for the collapsed toggle, replacing the selected option's icon. Use it when the collapsed control should read as one switch affordance rather than as a preview of the current selection — the collapsed navigation rail shows a repeat glyph this way. The button still announces the selected option as its accessible name, so the current state stays available to assistive tech. Only meaningful alongside `collapsible`.
</ParamField>

<ParamField path="collapsible" type="boolean" default="false">
  When `true`, the control automatically collapses to a single icon-only toggle whenever its container is too narrow to show every segment side by side, and expands again when the space returns. Collapsed, it shows `collapsedIcon` when given and otherwise the currently-selected option's `icon`; clicking it (or pressing Enter/Space) advances to the next option, wrapping around from the last back to the first.

  Only supported for the icon-bearing `"plain"` and `"brand"` appearances, and every option must define an `icon` unless `collapsedIcon` is given (there is nothing to show otherwise). Ignored, with a dev-time warning, when those conditions are not met.
</ParamField>

<ParamField path="defaultValue" type="string">
  Initially selected value (uncontrolled). Defaults to the first option.
</ParamField>

<ParamField path="disabled" type="boolean" default="false">
  Whether the control is disabled.
</ParamField>

<ParamField path="onChange" type="((value: string) => void)">
  Callback fired when the selected value changes.
</ParamField>

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

<ParamField path="value" type="string">
  Currently selected value (controlled).
</ParamField>

<ParamField path="variant" type="&#x22;fill&#x22; | &#x22;hug&#x22;" default="hug">
  Segment layout.
</ParamField>

## Exported types

Also exported from `@fanvue/ui`: `SegmentedControlAppearance`, `SegmentedControlOption`, `SegmentedControlProps`, `SegmentedControlSize`, `SegmentedControlVariant`.

***

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