SegmentedControl
A compact, equal-width control for switching between 2-4 mutually exclusive options. Inspired by iOS UISegmentedControl — ideal for view toggles, filter sets, and grouped selectors where Radio would feel too loose and Tabs too heavy.
Basic Usage
Sizes
Disabled segment
Individual options can be disabled via option.disabled. The whole control is implicitly disabled when only one option is selectable.
When to use which
| Component | Best for |
|---|---|
| SegmentedControl | 2-4 mutually exclusive options of similar weight (view toggles, filter sets) |
| Tabs (variant="segmented") | Same look, but bound to content panels via Tabs.Content |
| RadioGroup | 5+ options, or when labels need different lengths |
| Select (mobileVariant="sheet") | Many options, or options the user picks once and rarely changes |
The sliding indicator animation uses Pattern D (--tui-duration-base × --tui-ease-in-out) so the highlight glides between segments rather than snapping.
Mobile considerations
- All segments meet the 44px tap target on both
smandmdsizes (height scales up onpointer:coarse). - The indicator slide animation is GPU-accelerated (
transform), so it stays smooth even on mid-range Android. - For 5+ options, switch to
<Select mobileVariant="sheet">— SegmentedControl wraps awkwardly on narrow viewports.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value* | string | - | Currently selected option value (controlled) |
onChange* | (value: string) => void | - | Called when the user selects a different segment |
options* | SegmentedControlOption[] | - | { value, label, disabled? } — 2-4 recommended |
size | 'sm' | 'md' | 'sm' | Visual size |
aria-label | string | - | Accessible label for the segmented group |
className | string | - | Additional CSS class for the track element |
style | CSSProperties | - | Additional inline styles |