Tabs vs SegmentedControl
Both components let a user select one option from a set, and both share the same visual variants (pill, underline, outline). The difference is not visual — it is semantic. Choosing the wrong one produces incorrect screen reader announcements and breaks the ARIA contract.
The decision rule
Does selecting an option reveal a content panel that belongs to this component?
- Yes → use Tabs
- No → use SegmentedControl
Tabs
Use Tabs when the selection switches which content area is visible on the page. The panel is owned by the component — it lives directly below the tab list and its visibility is managed by the component itself.
<Tabs defaultValue="structure">
<Tabs.List aria-label="Course content">
<Tabs.Tab value="structure">Course Structure</Tabs.Tab>
<Tabs.Tab value="comments">Comments</Tabs.Tab>
<Tabs.Tab value="resources">Resources</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="structure">…</Tabs.Panel>
<Tabs.Panel value="comments">…</Tabs.Panel>
<Tabs.Panel value="resources">…</Tabs.Panel>
</Tabs>
ARIA pattern: role="tablist" / role="tab" / role="tabpanel". Each tab has aria-controls pointing to its panel; each panel has aria-labelledby pointing back to its tab. A screen reader announces: "Course Structure, tab, 1 of 3, selected".
Keyboard behaviour: Arrow keys move focus. Selection can be immediate (auto mode) or deferred to Enter/Space (manual mode, useful when panels are expensive to render).
Good use cases:
- Course page: Structure / Comments / Resources / Notes
- Settings page: General / Advanced / Security
- Admin screen with multiple content sections
SegmentedControl
Use SegmentedControl when the selection is a standalone choice that drives something external — a filter, a view mode, a sort order, a time period. The component has no attached content panels.
<SegmentedControl defaultValue="week" aria-label="Time period">
<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>
ARIA pattern: role="radiogroup" / role="radio". The selected item has aria-checked="true". A screen reader announces: "Week, radio button, 2 of 4, checked".
Keyboard behaviour: Arrow keys move focus and select simultaneously (standard radiogroup behaviour). There is no manual mode.
Good use cases:
- Day / Week / Month / Year period picker
- List / Grid / Board view switcher
- All / Active / Completed filter
- Reaction filter (All / 👍 / ❤️ / 👏)
Side-by-side comparison
| Aspect | Tabs | SegmentedControl |
|---|---|---|
| ARIA role | tablist / tab / tabpanel |
radiogroup / radio |
| Selection state | aria-selected |
aria-checked |
| Has content panels | Yes — Tabs.Panel |
No |
| Arrow key behaviour | Move focus (select on focus in auto mode) | Move focus and select immediately |
| Manual activation | Yes — Enter/Space to confirm | No |
| Disabled item behaviour | Native disabled, skipped in nav |
aria-disabled, kept visible in nav |
| Screen reader label | "tab, 1 of 3, selected" | "radio button, 1 of 3, checked" |
Why the visual overlap exists
Both components support the same visual variants because the appearance of a selected option (pill fill, underline indicator, border highlight) is a layout concern that is independent of its semantic role. A pill-shaped selector might be switching content panels (Tabs) or filtering a list (SegmentedControl) — the same visual affordance applies to both jobs.
The visual similarity is intentional design-system consistency. The semantic difference is what must be decided correctly before reaching for either component.
Common mistakes
Using Tabs as a filter. If you have a list of items and want to show All / Active / Completed, that is a filter — use SegmentedControl. There are no content panels; the list below is external state. Using Tabs here forces you to wrap the list in a Tabs.Panel, couples the list to the component, and announces "tab" to screen readers when "radio button" is correct.
Using SegmentedControl to navigate between content sections. If you have two panes of content on the same screen and want to toggle between them, that content belongs inside Tabs.Panel. Using SegmentedControl means managing panel visibility yourself, losing the built-in aria-controls / aria-labelledby pairing, and announcing "radio button" when "tab" is correct.
Choosing based on visual style. The underline variant of Tabs and the underline variant of SegmentedControl look nearly identical. The choice is never about which looks right — it is always about which semantic model fits the interaction.