---
title: "Segmented Controls"
description: "A linear set of two or more segments used to switch between mutually exclusive options or views."
source: "Equality"
---
import {
  SegmentedControlsTextDemo,
  SegmentedControlsIconDemo,
  SegmentedControlsSuffixDemo,
  SegmentedControlsIconOnlyDemo,
  SegmentedControlsSizesDemo,
  SegmentedControlsVariantsDemo,
} from "@demo/components/demo/segmented-controls";

## Overview

Segmented Controls present a developer-defined set of two or more mutually exclusive
options in a single, connected control. They are commonly used to switch between views,
filters, or modes where a dropdown would be excessive.

Each option supports a prefix `icon`, a required text `label`, and an optional `suffix`
slot. The currently selected option is highlighted using the primary button color.

## Usage

Import the component:

```tsx
import { SegmentedControls } from "@eqtylab/equality";
```

`options`, `value`, and `onValueChange` are required. `value` and `onValueChange` should be
controlled by the parent component.

```tsx
const [view, setView] = useState("day");

<SegmentedControls
  value={view}
  onValueChange={setView}
  options={[
    { value: "day", label: "Day" },
    { value: "week", label: "Week" },
    { value: "month", label: "Month" },
  ]}
/>;
```

## Examples

### Text only

<SegmentedControlsTextDemo client:only="react" />

### With prefix icons

Provide an `icon` (a [Lucide](https://lucide.dev) icon name or a React element) to render a
prefix before the label.

<SegmentedControlsIconDemo client:only="react" />

### With a suffix slot

Use the `suffix` slot to render supplementary content, such as a count `Badge`, after the label.

<SegmentedControlsSuffixDemo client:only="react" />

## Variants

Give an option a `variant` to change the indicator's colour while that option is selected: `primary` (the default), `neutral`, `success`, `warning`, or `danger`. Use this when the options represent states, such as filtering results by status, so the selection carries the same meaning as the rest of the UI.

<SegmentedControlsVariantsDemo client:only="react" />

### Usage

```tsx
<SegmentedControls
  value={status}
  onValueChange={setStatus}
  options={[
    { value: "primary", label: "Primary" },
    { value: "success", label: "Success", icon: "Check", variant: "success" },
    {
      value: "warning",
      label: "Warning",
      icon: "OctagonAlert",
      variant: "warning",
    },
    {
      value: "danger",
      label: "Danger",
      icon: "TriangleAlert",
      variant: "danger",
    },
    { value: "neutral", label: "Neutral", variant: "neutral" },
  ]}
/>
```

## Display modes

Like the [Badge](/components/badge) component, the `display` prop controls what is rendered
inside each segment: `both` (the default), `text-only`, or `icon-only`.

`icon-only` requires **every** option to define an `icon`, and it falls back to `both` if any
option is missing one. Because `label` is mandatory, it is used as the accessible label
(`aria-label`) and tooltip for each segment in `icon-only` mode.

### Icon only

<SegmentedControlsIconOnlyDemo client:only="react" />

```tsx
<SegmentedControls
  display="icon-only"
  value={view}
  onValueChange={setView}
  options={[
    { value: "grid", label: "Table view", icon: "Grid3X3" },
    { value: "list", label: "List view", icon: "List" },
  ]}
/>
```

## Sizes

The `size` prop accepts `sm`, `md` (the default), or `lg`. Use the size that makes sense for your UI's density and is in-line with neighboring components.

<SegmentedControlsSizesDemo client:only="react" />

### Usage

```tsx
<SegmentedControls size="sm" value={view} onValueChange={setView} options={options} />
<SegmentedControls size="md" value={view} onValueChange={setView} options={options} />
<SegmentedControls size="lg" value={view} onValueChange={setView} options={options} />
```

## Props

| Name            | Description                                           | Type                             | Default | Required |
| --------------- | ----------------------------------------------------- | -------------------------------- | ------- | -------- |
| `options`       | The set of segments to render.                        | `SegmentedControlOption[]`       | -       | ✅       |
| `value`         | The value of the currently selected option.           | `string`                         | -       | ✅       |
| `onValueChange` | Called with the new value when a segment is selected. | `(value: string) => void`        | -       | ✅       |
| `display`       | Controls what is rendered inside each segment.        | `both`, `text-only`, `icon-only` | `both`  | ❌       |
| `size`          | Height of the control, matching `Button` sizes.       | `sm`, `md`, `lg`                 | `md`    | ❌       |

### `SegmentedControlOption`

| Name      | Description                                                     | Type                                                 | Default   | Required |
| --------- | --------------------------------------------------------------- | ---------------------------------------------------- | --------- | -------- |
| `value`   | Unique value used to identify the option.                       | `string`                                             | -         | ✅       |
| `label`   | Text label. Also used as the accessible label when `icon-only`. | `string`                                             | -         | ✅       |
| `icon`    | Prefix icon: a Lucide icon name or a React element.             | `string`, `React.ReactElement`                       | -         | ❌       |
| `suffix`  | Content rendered after the label.                               | `React.ReactNode`                                    | -         | ❌       |
| `variant` | Colour of the indicator while this option is selected.          | `primary`, `neutral`, `success`, `warning`, `danger` | `primary` | ❌       |