# Segmented Control

A segmented control switches between a few options, such as views of the same content, with exactly one always selected.

A segmented control shows a small set of options in one bar and keeps exactly one selected, such as list or grid for the same items. Changing the selection changes what people see straight away. Switching between separate panels of content is [Tabs](/docs/tabs), and a choice inside a form is a [Radio Group](/docs/radio-group).


## Usage

```tsx
import { Flex, Heading, SegmentedControl } from '@kushagradhawan/kookie-ui';

export function ProjectsHeader({ view, setView }: { view: string; setView: (view: string) => void }) {
  return (
    <Flex justify="between" align="center">
      <Heading size="4">Projects</Heading>
      <SegmentedControl.Root value={view} onValueChange={setView} aria-label="View">
        <SegmentedControl.Item value="list">List</SegmentedControl.Item>
        <SegmentedControl.Item value="grid">Grid</SegmentedControl.Item>
        <SegmentedControl.Item value="board">Board</SegmentedControl.Item>
      </SegmentedControl.Root>
    </Flex>
  );
}
```

## Anatomy

```tsx
<SegmentedControl.Root>
  <SegmentedControl.Item value="a">Option</SegmentedControl.Item>
</SegmentedControl.Root>
```

- **SegmentedControl.Root:** The bar. It holds the selected value and draws the indicator that slides to the selected item.
- **SegmentedControl.Item:** One option. Its text or icon is the label.

## Best practices

**Keep it to two to five options.** Every option shares one bar, so each one gets narrower as you add more. For a longer list, use a [Select](/docs/select).

**Use one or two words per label.** Items share the width evenly, and a long label makes the whole bar wide. Use nouns of the same kind, such as "Day, Week, Month".

**Always start with a value selected.** One option is always on, and people can't clear it by pressing it again. Set `defaultValue` or `value` so the bar never renders empty.

**Change the view at once.** A segmented control filters or rearranges what is on screen now. Don't use it for a choice that waits for a save button, which is a [Radio Group](/docs/radio-group).

**Don't use it for actions.** Each item selects a state. An action such as "Export" is a [Button](/docs/button), and an on or off state such as bold is a [Toggle](/docs/toggle).

## Variant

`variant` sets the bar's look. `soft` is the default, a filled track. `raised` and `outline` wear the edge of a [TextField](/docs/text-field) of the same variant, with a soft indicator inside, so a panel of fields has no filled track in it.

## Size

`size` picks one of four heights: 28, 32, 40 and 48 pixels. Size 2 is the default. The bar is as tall as a [Button](/docs/button) of the same size, so it lines up in a toolbar. `size` accepts a responsive object, such as `{ initial: '3', md: '2' }`.

## Orientation

`orientation="vertical"` stacks the items in a column, and the indicator slides up and down. Use it with `iconOnly` items in a narrow side panel, where short square items fit. Text labels in a vertical bar read poorly unless they are one short word. `orientation` accepts a responsive object.

## Examples

### Icon only

`iconOnly` makes each item a square as wide as it is tall. The item has no visible text, so give each one an `aria-label`.

```tsx
<SegmentedControl.Root defaultValue="left" aria-label="Text alignment">
  <SegmentedControl.Item value="left" iconOnly aria-label="Align left">
    <HugeiconsIcon icon={TextAlignLeftIcon} strokeWidth={1.75} />
  </SegmentedControl.Item>
  <SegmentedControl.Item value="center" iconOnly aria-label="Align centre">
    <HugeiconsIcon icon={TextAlignCenterIcon} strokeWidth={1.75} />
  </SegmentedControl.Item>
  <SegmentedControl.Item value="right" iconOnly aria-label="Align right">
    <HugeiconsIcon icon={TextAlignRightIcon} strokeWidth={1.75} />
  </SegmentedControl.Item>
</SegmentedControl.Root>
```

### Icon and label

Put the icon before the text, inside the item. The item sizes the icon, so don't size it yourself.

```tsx
<SegmentedControl.Root defaultValue="grid" aria-label="View">
  <SegmentedControl.Item value="list">
    <HugeiconsIcon icon={Menu01Icon} strokeWidth={1.75} />
    List
  </SegmentedControl.Item>
  <SegmentedControl.Item value="grid">
    <HugeiconsIcon icon={GridViewIcon} strokeWidth={1.75} />
    Grid
  </SegmentedControl.Item>
</SegmentedControl.Root>
```

### Time range

A row of periods above a chart is a common use. The chart redraws as soon as the selection changes.

```tsx
<SegmentedControl.Root value={range} onValueChange={setRange} aria-label="Time range">
  <SegmentedControl.Item value="day">Day</SegmentedControl.Item>
  <SegmentedControl.Item value="week">Week</SegmentedControl.Item>
  <SegmentedControl.Item value="month">Month</SegmentedControl.Item>
  <SegmentedControl.Item value="year">Year</SegmentedControl.Item>
</SegmentedControl.Root>
```

## Props

### SegmentedControl.Root

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | | The selected value, controlled |
| `defaultValue` | `string` | | The selected value at first render |
| `onValueChange` | `(value: string) => void` | | Fires with the new value. It never fires with an empty value |
| `variant` | `'soft' \| 'raised' \| 'outline'` | `'soft'` | The look of the bar |
| `size` | `'1' \| '2' \| '3' \| '4'`, responsive | `'2'` | The height step |
| `orientation` | `'horizontal' \| 'vertical'`, responsive | `'horizontal'` | The direction of the bar |
| `radius` | `'none' \| 'small' \| 'medium' \| 'large' \| 'full'` | Theme radius | The corner |
| `disabled` | `boolean` | `false` | Disables every item |

SegmentedControl.Root also accepts every attribute of the HTML `div` element.

### SegmentedControl.Item

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | | Required. The value this item selects |
| `iconOnly` | `boolean` | `false` | Square, icon only. Give the item an `aria-label` |
| `disabled` | `boolean` | `false` | Stops this item from being selected |

SegmentedControl.Item also accepts every attribute of the HTML `button` element.

## Accessibility

| Key | Action |
| --- | --- |
| Tab | Moves focus into the bar, onto the selected item, and out again |
| Arrow keys | Move to the next or previous item and select it |
| Home or End | Moves to the first or last item and selects it |
| Space or Enter | Selects the focused item |

- **Name:** Give the root an `aria-label`, such as "View". Each item takes its name from its text, or from its `aria-label` when it is icon only.
- **State:** The root has `role="radiogroup"` and each item has `role="radio"`. A screen reader announces the item, whether it is selected, and its position in the group.
- **Focus:** The bar is one tab stop. Arrow keys select as they move, as in a radio group.
- **Disabled:** A disabled bar sets `disabled` on every item, which removes them from the tab order. Arrow keys skip a disabled item.
