# Segmented control

> A compact single choice with a pill that slides to the selection.

Component · Free · https://lodeui.com/components/segmented-control

## When to use

- Two to five mutually exclusive options that should all be visible, like 7D / 30D / 90D.
- Switching a view (list, board, calendar) or a billing cycle.

## When not to use

- More than five options or long labels: use a select.
- Options that trigger actions rather than set a value: use buttons.
- Navigation between pages: use tabs or links.

## Installation

```bash
npx shadcn@latest add https://lodeui.com/r/segmented-control.json
```

npm dependencies: motion

## Usage

```tsx title="example.tsx"
import { SegmentedControl } from "@/components/ui/segmented-control";

export function RangePicker({ value, onChange }: { value: "7" | "30" | "90"; onChange: (v: "7" | "30" | "90") => void }) {
  return (
    <SegmentedControl
      aria-label="Range"
      value={value}
      onValueChange={onChange}
      options={[
        { value: "7", label: "7 days" },
        { value: "30", label: "30 days" },
        { value: "90", label: "90 days" },
      ]}
    />
  );
}
```

## API reference

### SegmentedControl

A radio group drawn as a pill track. Generic over the option value type, so onValueChange is typed.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| options | `readonly { value: T; label: ReactNode }[]` | – | The choices, in order. Labels can include icons. |
| value / defaultValue | `T` | – | Controlled or uncontrolled value. Defaults to the first option. |
| onValueChange | `(value: T) => void` | – | Called with the newly selected value. |
| size | `"sm" \| "md"` | `"md"` | 36px or 44px tall. |
| aria-label | `string` | – | Names the group for screen readers. Required unless a visible label is linked. |

## Keyboard interactions

| Keys | Action |
| --- | --- |
| ← / → | Select the previous or next option |
| ↑ / ↓ | Same as left and right |
| Home / End | Select the first or last option |

## Accessibility

- role="radiogroup" with role="radio" items and aria-checked.
- Roving tabindex: Tab enters the group once, arrows move inside it.
- The selected option is not conveyed by color alone; the pill has a ring and shadow.

## Motion

- The pill is a shared-layout element that springs between options (bounce 0.15, 450ms).
- Text color crossfades in 200ms.

## Responsive behavior

- Width hugs the labels. Add className="w-full [&>button]:flex-1" to stretch it on phones.
- Keep labels short (one or two words) so it fits at 320px.

## Performance

- One animated element per control; options are plain buttons.

## Notes for AI

- Always pass aria-label.
- Use `as const` on the options array to get a literal union for T.

## Related

- [Tabs](https://lodeui.com/components/tabs) — Underline or pill tabs with an indicator that glides between them.
- [Radio group](https://lodeui.com/components/radio-group) — Radios as a list or as choice cards, with arrow-key navigation.
- [Select](https://lodeui.com/components/select) — A pill trigger with a springy menu, typeahead, and full keyboard support.
- [Switch](https://lodeui.com/components/switch) — A toggle whose thumb stretches as you press it.
