# 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.

```tsx
<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.

```tsx
<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.

---

Interactive version: https://storybook-tangible-ui.pages.dev/?path=/docs/guides-tabs-vs-segmentedcontrol--docs
