A row of connected buttons where exactly one option is chosen, for switching a view or a setting with two to five choices. Use SegmentedControlItem for each option.
import { SegmentedControl, SegmentedControlItem } from "@rdloom/react";
export default function SegmentedControlBasicExample() {
return (
<SegmentedControl label="View" defaultSelectedKey="list">
<SegmentedControlItem id="list">List</SegmentedControlItem>
<SegmentedControlItem id="board">Board</SegmentedControlItem>
<SegmentedControlItem id="calendar">Calendar</SegmentedControlItem>
</SegmentedControl>
);
}Installation
npx rdloom add segmented-controlCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/segmented-control.jsonWorks, but without upgrade tracking.
Usage
import { SegmentedControl, SegmentedControlItem } from "@rdloom/react";
<SegmentedControl label="View" defaultSelectedKey="list">
<SegmentedControlItem id="list">List</SegmentedControlItem>
<SegmentedControlItem id="board">Board</SegmentedControlItem>
<SegmentedControlItem id="calendar">Calendar</SegmentedControlItem>
</SegmentedControl>Controlled
$19 a month.
import { useState } from "react";
import { SegmentedControl, SegmentedControlItem } from "@rdloom/react";
export default function SegmentedControlControlledExample() {
const [billing, setBilling] = useState<string>("monthly");
return (
<div className="flex flex-col items-center gap-3 text-sm">
<SegmentedControl label="Billing period" selectedKey={billing} onChange={(key) => setBilling(String(key))}>
<SegmentedControlItem id="monthly">Monthly</SegmentedControlItem>
<SegmentedControlItem id="yearly">Yearly</SegmentedControlItem>
</SegmentedControl>
<p aria-live="polite">{billing === "yearly" ? "$190 a year, two months free." : "$19 a month."}</p>
</div>
);
}Sizes
import { SegmentedControl, SegmentedControlItem } from "@rdloom/react";
export default function SegmentedControlSizesExample() {
return (
<div className="flex flex-col items-center gap-3">
<SegmentedControl label="Density, small" size="sm" defaultSelectedKey="compact">
<SegmentedControlItem id="compact">Compact</SegmentedControlItem>
<SegmentedControlItem id="comfortable">Comfortable</SegmentedControlItem>
</SegmentedControl>
<SegmentedControl label="Density, medium" size="md" defaultSelectedKey="comfortable">
<SegmentedControlItem id="compact">Compact</SegmentedControlItem>
<SegmentedControlItem id="comfortable">Comfortable</SegmentedControlItem>
</SegmentedControl>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
labelrequiredWhat the control chooses, e.g. 'View'. It is the accessible name of the group. | string | none |
childrenrequiredSegmentedControlItem elements, each with an id. | node | none |
selectedKeyControlled selected item id. | Key | none |
defaultSelectedKeyInitial selected item id when uncontrolled. | Key | none |
onChangeCalled with the id of the chosen item. | (key: Key) => void | none |
sizeControl height. | "sm" | "md" | "md" |
isDisabledDisables every option. | boolean | false |
Accessibility
Role radiogroup, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab enters the group, on the chosen option
- Arrow keys move to and choose the next or previous option
- Space chooses the focused option
Screen readers announce
- Announced as a radio group with the label, then each option as "radio button, N of M, checked or not checked"
- Moving with arrow keys announces the newly chosen option
What your code must do
- A radio group named by label, with each option a radio
- The chosen option is marked by fill and weight and by aria-checked, not only by color
- Exactly one option is always chosen
- Selected text meets 4.5:1 against its background
Guidelines
Use it when
- Switching between views of the same data: List, Board, Calendar
- A setting with two to five short options, like Monthly or Yearly
Avoid it when
- More than five options or long labels: use Select or RadioGroup
- Choosing several at once: use checkboxes
- Navigating to different pages: use Tabs or links
Don't
- Using it as page navigation
- Options that are not mutually exclusive
- Icon-only options without a text label
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-default--rd-color-surface-subtle--rd-color-border-default--rd-color-action-primary--rd-color-action-on-primary--rd-color-text-default--rd-color-focus-ring--rd-radius-control