# Segmented Control

> A React iOS-style segmented control with a raised thumb that slides and follows your finger.

- Type: React component (Inputs)
- Access: Free (the source is open to everyone)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add segmented-control`
- Docs: https://wingo-ui.com/components/segmented-control

## Interaction

Click or tap an option and the raised thumb glides over to it on a quick spring; the hairlines next to it fade away. Press the thumb and it dips a little; keep holding and slide sideways to drag it across the options, the value follows the finger and settles where you let go. A vertical swipe still scrolls the page. With the keyboard the control is one tab stop: Left and Right (Up and Down too) move and select, Home and End jump to the ends, and disabled options are skipped. Icon-only options show their name in a tooltip on hover and focus, or on a long press on touch. On a phone the medium size is 44px tall and fullWidth shares the width between options. With dir set to rtl the arrows and the drag mirror.

## Installation

```bash
npx wingo-ui@latest add segmented-control
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/segmented-control.json`

Files:

- `components/ui/segmented-control.tsx`

npm dependencies: `npm i @radix-ui/react-radio-group motion`

Wingo UI dependencies (installed automatically):

- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Tooltip](https://wingo-ui.com/components/tooltip) ([Markdown](https://wingo-ui.com/components/tooltip.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { useState } from "react"
import { SegmentedControl, type SegmentedControlOption } from "@/components/ui/segmented-control"

const periods: SegmentedControlOption[] = [
  { value: "day", label: "Day" },
  { value: "week", label: "Week" },
  { value: "month", label: "Month" },
  { value: "year", label: "Year" },
]

export function SalesPeriod() {
  const [period, setPeriod] = useState("week")
  return (
    <SegmentedControl
      aria-label="Period"
      options={periods}
      value={period}
      onValueChange={setPeriod}
      fullWidth
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `SegmentedControlOption[]` |  | The options: { value, label?, icon?, disabled?, badge?, tooltip? }. 2 to 5 read best; more still fit with truncated labels (use tabs then). A number badge renders a small count that hides at 0. |
| `value?` | `string` |  | The selected option, when you control it. A value that is not in options shows no thumb. |
| `defaultValue?` | `string` | `""` | The option selected on the first render when uncontrolled. Empty, or a value that is not in options, starts on the first enabled option. |
| `onValueChange?` | `(value: string) => void` |  | Called with the new value on click, arrow keys, Home / End and while dragging the thumb. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Track height 32 / 40 / 48px with matching type and icon size; options fill it inside a 2px padding. On touch screens md grows to 44px and sm keeps its look with a 44px hit area. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corner shape of the track, scaled with the size like the button's. The thumb is 2px rounder-in so the gap stays even. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The thumb's color. neutral is a raised white surface (a lifted gray in dark mode) like iOS; the other tones fill the thumb and turn its label to the tone's foreground. |
| `color?` | `string` | `""` | Any CSS color for the thumb. Replaces the tone; the label on it turns black or white by lightness. Leave empty to use the tone. |
| `dividers?` | `boolean` | `true` | Hairlines between unselected neighbours. The ones touching the thumb fade out, like iOS. |
| `iconOnly?` | `boolean` | `false` | Shows only the icons. Each label becomes the option's aria-label and its tooltip, so the control stays readable. |
| `fullWidth?` | `boolean` | `false` | Stretches the track to its container and shares the width between options. The usual layout on a phone. |
| `draggable?` | `boolean` | `true` | Press the thumb and slide sideways to change the value, with touch or a mouse. Vertical swipes still scroll the page. |
| `loop?` | `boolean` | `true` | Arrow keys wrap from the last option to the first and back. Off, they stop at the ends. |
| `disabled?` | `boolean` | `false` | Dims the whole control and blocks it. Disable single options with option.disabled. |
| `aria-label?` | `string` | `""` | The group's name for screen readers. Required when no visible label names the control. |
| `name?` | `string` | `""` | Form field name. Inside a <form> the selected value is submitted like a radio group. |
| `dir?` | `"ltr" \| "rtl"` | `"ltr"` | Reading direction; rtl mirrors the layout, the arrow keys and the drag. Left out, it follows a Radix DirectionProvider, else ltr. |
| `classNames?` | `Partial<Record<"item" \| "thumb" \| "icon" \| "label" \| "badge" \| "divider", string>>` |  | Extra classes for the inner parts. |
| `className?` | `string` |  | Extra classes for the track, merged last. |

## Parts

### SegmentedControlOption (type)

One option: value, label, icon, disabled, badge (a number renders a count badge, hidden at 0) and tooltip (shown on hover, focus and long press).

## Examples

- **Sizes and radii**: 32, 40 and 48px tracks; the thumb stays 2px rounder-in at every radius.
- **Icon-only view switch**: Labels move into aria-labels and tooltips (hover, focus or long press).
- **Full width on a phone**: fullWidth shares the width; drag the thumb across with a finger.
- **Tones and a custom color**: neutral is the raised iOS thumb; tones and any CSS color fill it.
- **With badges**: A number renders a count badge that tints with the thumb.
- **In a form**: name="delivery" submits the value like a radio group.
- **Disabled**: A single option locked behind a plan, and a whole disabled group.

## See also

- [Tabs](https://wingo-ui.com/components/tabs) ([Markdown](https://wingo-ui.com/components/tabs.md))
- [Radio Group](https://wingo-ui.com/components/radio-group) ([Markdown](https://wingo-ui.com/components/radio-group.md))
- [Toggle](https://wingo-ui.com/components/toggle) ([Markdown](https://wingo-ui.com/components/toggle.md))
- [Tooltip](https://wingo-ui.com/components/tooltip) ([Markdown](https://wingo-ui.com/components/tooltip.md))
- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))

---

Canonical: https://wingo-ui.com/components/segmented-control
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
