Capítulo 33 de 43

Chapter 33: Tabs

Core Idea

Layered content panels shown one at a time, selected via a horizontally or vertically arranged set of triggers.

Key Concepts

  • Anatomy: RootList (→ Triggers) + one Content per Trigger, matched by shared value.
  • activationMode (default "automatic"): automatic means arrow-key focus movement also activates the tab immediately; "manual" requires an explicit Enter/Space to activate after arrowing to a trigger — matters for expensive-to-render tab content.
  • List's loop (default true): arrow-key wrap-around at the ends.
  • Content's forceMount: keep inactive tab content mounted (e.g. to preserve scroll position or avoid remount cost) instead of Radix's default unmount-when-inactive behavior.

Code Examples

<Tabs.Root defaultValue="tab1" orientation="vertical">
  <Tabs.List aria-label="tabs example">
    <Tabs.Trigger value="tab1">One</Tabs.Trigger>
    <Tabs.Trigger value="tab2">Two</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="tab1">Tab one content</Tabs.Content>
  <Tabs.Content value="tab2">Tab two content</Tabs.Content>
</Tabs.Root>
  • What it demonstrates: vertical-orientation tabs — same anatomy as horizontal, just the orientation prop and CSS layout change.

Key Takeaways

  1. Switch to activationMode="manual" when tab content is expensive to render/fetch — otherwise every arrow-key press triggers a content swap, not just a focus move.
  2. List needs an aria-label (or aria-labelledby) since it doesn't have implicit accessible text of its own.
  3. Use forceMount on Content when you need inactive panels to stay in the DOM (e.g. preserving an iframe's or a video's playback state across tab switches).

Connects To

  • Navigation Menu: a related but distinct primitive for hierarchical site navigation rather than same-page content panels.