Components
Segmented Control
Mutually exclusive options presented as equal-width segments
SegmentedControl lets users switch between a small set of closely related views or content subsets within the same context — for example toggling between “List”, “Grid”, and “Map” views, or refining a list with filters like “All”, “Active”, and “Archived”. Options are mutually exclusive and exactly one segment is always selected; selecting a new segment automatically deselects the previous one, and the selection cannot be cleared. It is built on React Aria and styled with Thumbprint v2 semantic tokens, so it supports dark mode automatically.
Use a maximum of three segments with short, parallel, text-only labels. If you need more options, iconography, or navigation across pages, reach for Tabs, Dropdown, or Chip instead.
Basic segmented control
Provide value and onChange for a controlled component. Each Segment reports its value when selected. The control fills its container’s width and every segment receives equal width, with the selected pill sliding horizontally to the new segment.
function SegmentedControlExample() { const [value, setValue] = React.useState('list'); return ( <SegmentedControl value={value} onChange={setValue} accessibilityLabel="View"> <Segment value="list">List</Segment> <Segment value="grid">Grid</Segment> <Segment value="map">Map</Segment> </SegmentedControl> ); }
Uncontrolled usage
For an uncontrolled component, set the initial selection with defaultValue. When neither value nor defaultValue is provided, the first segment is selected, since a segmented control always has a selection.
<SegmentedControl defaultValue="edit" accessibilityLabel="Mode"> <Segment value="view">View</Segment> <Segment value="edit">Edit</Segment> <Segment value="preview">Preview</Segment> </SegmentedControl>
Sizes
The size prop switches between the large (default, 48px) and small (40px) controls. Choose based on the surrounding UI density. The large control fills its container’s width, while the small control hugs its content; segments receive equal width in both sizes.
<SegmentedControl size="small" defaultValue="all" accessibilityLabel="Filter"> <Segment value="all">All</Segment> <Segment value="active">Active</Segment> <Segment value="archived">Archived</Segment> </SegmentedControl>
Narrow screens
Labels are never truncated. Segments share the container’s width equally while everything fits, but a segment never shrinks below its label’s width — on narrow viewports such as mobile web, the control instead scrolls horizontally (swipe on touch devices) and the selected segment is kept in view. The example below uses deliberately long labels: resize the browser window (or open this page on a phone) to see the full labels preserved and the control become scrollable. Keep real labels to one or two short, parallel words so scrolling is rarely needed; if the labels can’t be shortened, reconsider the options or use Tabs or Dropdown instead.
function SegmentedControlExample() { const [value, setValue] = React.useState('recommendations'); return ( <SegmentedControl value={value} onChange={setValue} accessibilityLabel="Section"> <Segment value="recommendations">Recommendations for you</Segment> <Segment value="availability">Availability calendar</Segment> <Segment value="messages">Unread messages</Segment> </SegmentedControl> ); }
Disabled segments
Set isDisabled on a Segment to disable that segment. Disabled segments are visually subdued and cannot be selected.
<SegmentedControl defaultValue="list" accessibilityLabel="View"> <Segment value="list">List</Segment> <Segment value="grid" isDisabled> Grid </Segment> <Segment value="map">Map</Segment> </SegmentedControl>
Accessibility
- The control renders as a
radiogroupwith oneradioper segment, conveying the single-selection pattern to screen readers. Label the group withaccessibilityLabel. - Keyboard support follows the React Aria toolbar pattern: arrow keys move focus between segments, and
SpaceorEnterselects the focused segment. - A visible focus indicator appears when navigating with a keyboard, and the sliding selection animation is disabled for users with
prefers-reduced-motionenabled.
The full props for SegmentedControl and Segment are listed in the tables below.
Props
Segment
valuerequiredThe value reported to the group's
onChangewhen this segment is selected. Must be unique within the group.TypestringchildrenrequiredThe segment's text label. Keep labels short (1–2 words); segments are text-only.
TypestringisDisabledDisables this individual segment. Maps to React Aria's
isDisabled.TypebooleanaccessibilityLabelAccessible label for the segment, for when the visible text label isn't descriptive enough on its own. Maps to React Aria's
aria-label.TypestringdataTestIdA selector hook into the React component for use in automated testing environments.
Typestring
SegmentedControl
childrenrequiredTwo or three
Segmentoptions. Segments must be direct children (not wrapped in fragments or intermediate components).TypeReact.ReactNodevalueThe selected segment's
value. Provide together withonChangefor a controlled control.TypestringdefaultValueThe initially selected value for an uncontrolled control. Defaults to the first segment's
value, since a segmented control always has exactly one selected segment.TypestringonChangeFunction that runs when the selected segment changes. It receives the newly selected segment's
value. Not called when the already-selected segment is pressed again — the selection cannot be cleared.Type(value: string) => voidsizeSets the size of the control.
'large'(the default) is the 48px-tall control that fills its container's width;'small'is the 40px-tall control that hugs its content. Maps to the[data-size]attribute.Type'small' | 'large'Default'large'accessibilityLabelAccessible label for the group. Maps to React Aria's
aria-label.TypestringdataTestIdA selector hook into the React component for use in automated testing environments. Applied to the group's root element.
Typestring