Single-select switcher for 2 to 5 inline options, with a flat row or grouped pill style.
Description
SegmentedControl is a compound component (SegmentedControl + SegmentedControlItem) that renders a <div role="group"> of real <button> items. It has two visual modes: flat is a row of independent buttons, grouped is the classic segmented pill with a sliding indicator that animates between the active option.
Reach for it when you have 2 to 5 mutually exclusive options that benefit from being visible all at once. Typical surfaces: view switchers (grid, list, chart), time-range pickers (day, week, month), layout toggles, and inline tabs that don't change the route.
If the list grows past about 5 options, use Select. If each option swaps a route or a content panel, use Tabs. If the choice is a boolean, use Switch. If users can pick more than one, use ToggleGroup or CheckboxGroup. If you need the standard single-tab-stop arrow-key radio model, use RadioGroup.
Installation
Anatomy
SegmentedControl provides a context that hands variant, shape, size, and appearance down to its items, plus an optional value binding. Each SegmentedControlItem is a self-contained <button> (or any element via asChild).
Usage
Examples
Variants
variant="primary" paints the active option with the primary token; variant="neutral" keeps it grayscale. Pick neutral when the segmented control sits next to other primary actions you don't want to compete with.
Appearance
appearance="flat" is a row of independent buttons styled with bg-weak at rest; activate them yourself with the selected prop on the chosen item. appearance="grouped" shares a single rounded container and animates a sliding indicator between the active item.
Sizes
Five sizes: xs, sm, md (default), lg, xl. Heights, paddings, type, and item radii all scale together.
Shape
shape="rounded" (default) follows the size's radius scale; shape="pill" makes both the container and the items fully rounded.
With Icon
Pass leadingIcon or trailingIcon to any item. Icon sizing is driven by the active size, so you don't size the SVG yourself.
Icon Only
Set iconOnly on each item for a square footprint with no horizontal padding. Always pair with aria-label so the button announces its purpose.
Controlled
Pass value and onValueChange to drive selection from your own state. Use uncontrolled defaultValue for simple toggles; reach for controlled when other UI needs to react to the change.
Accessibility
The root is a role="group". Each item is a real <button> with aria-pressed reflecting the active option and aria-disabled when disabled. Every item is its own tab stop (there is no roving tabindex); if you need single-tab-stop arrow-key navigation, use RadioGroup instead.
ARIA notes:
- The root sets
role="group". Pair it witharia-labeloraria-labelledbywhen the surrounding context doesn't already describe the choice. - Items set
aria-pressedtotrueorfalseso screen readers announce the toggle state. - When
iconOnlyis set, the item has no visible text, soaria-labelis required on each item. - Disabled items get
aria-disabledand skip click handlers.
Styling
Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):
Data slots and attributes: the component sets these for CSS targeting:
data-slot="segmented-control"on the root element.data-slot="segmented-control-indicator"on the sliding pill (only rendered whenappearance="grouped").data-slot="segmented-control-item"on each item.data-slot="segmented-control-item-icon"on each leading and trailing icon wrapper.data-slot="segmented-control-item-content"on the inner text wrapper.data-variant="<variant>",data-shape="<shape>",data-size="<size>",data-appearance="<appearance>"on both root and item.data-state="on" | "off"on each item;data-value="<value>"on each item.
Target the active item in CSS:
Related Components
- RadioGroup: use this for the standard single-tab-stop arrow-key radio model with a hidden native input.
- Tabs: use this when each option swaps a content panel or routes to a different view.
- ToggleGroup: use this when users can select more than one option at a time.
- Switch: use this when the choice is a boolean.
- Select: use this when the list grows past about 5 options.
API Reference
SegmentedControl
Root that owns the selection state and provides the variant context. Extends React.ComponentProps<"div">, so standard div attributes (id, aria-label, aria-labelledby, etc.) are accepted.
Props
Variants
SegmentedControlItem
One option in the control. Extends Omit<React.ComponentProps<"button">, "value">. Renders a <button> by default, or any element via asChild (Radix Slot).