Tabs
A set of layered content panels — one shown at a time — switched by an accessible tab list.
Description
Tabs is a compound component (Tabs + TabsList + TabsTrigger + TabsContent) built on the Radix Tabs primitive. The list is a real role="tablist" of role="tab" buttons; each TabsContent is a role="tabpanel" linked to its trigger, and only the active panel is shown. Selecting a trigger swaps the visible panel in place.
Reach for it when each option reveals a content panel within the same surface (settings forms, a detail card with sub-views, an editor with modes). The triggers use the same line styling as TabMenu: a primary underline that slides to the active tab.
If your options instead navigate to routes or sections elsewhere on the page (no in-place panel), use TabMenu — it renders the tab list only and supports asChild links. For 2 to 5 inline, mutually exclusive options without rich item content, use SegmentedControl.
Installation
Anatomy
Tabs owns the selected value and the size context. TabsList wraps the triggers; each TabsTrigger is paired by value to a TabsContent panel.
Usage
Examples
Controlled
Pass value and onValueChange to drive selection from your own state — useful when other UI needs to react to the active tab, or to persist it to the URL. Omit them (use defaultValue) for a simple uncontrolled switcher.
Accessibility
Built on the Radix Tabs primitive, so it follows the WAI-ARIA Tabs pattern: the list is role="tablist", triggers are role="tab" with aria-selected, and each panel is role="tabpanel" wired to its trigger via aria-controls / aria-labelledby. Focus uses a roving tabindex — one tab stop for the whole list, arrow keys move between tabs.
ARIA notes:
- Activation is automatic by default (focusing a tab selects it). Pass
activationMode="manual"onTabsto requireEnter/Space. - Disabled triggers are skipped by arrow-key navigation.
- If a trigger only shows an icon, add an
aria-labelso it announces its purpose.
Styling
Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()).
Data slots and attributes: the component sets these for CSS targeting.
data-slot="tabs"on the root,data-slot="tabs-list"on the list,data-slot="tabs-trigger"on each trigger,data-slot="tabs-content"on each panel.data-slot="tabs-active-line"on the sliding active indicator;data-slot="tabs-trigger-label"on the label;data-slot="tabs-trigger-icon"(withdata-position="leading" | "trailing") on each icon.data-size="<size>"on the root and each trigger.- Radix sets
data-state="active" | "inactive"on the relevant elements.
Target the active trigger in CSS:
Related Components
- TabMenu: use this when the options navigate (routes or in-page sections) instead of swapping a panel in place; supports
asChildlinks. - SegmentedControl: use this for 2 to 5 inline mutually exclusive options without rich item content.
- Accordion: use this to expand multiple stacked regions at once rather than one panel at a time.
API Reference
Tabs
Root that owns selection state and the size context. Wraps Radix Tabs.Root, so its props (dir, activationMode, etc.) are accepted.
Props
TabsList
Wraps Radix Tabs.List. Renders role="tablist" and the baseline border.
TabsTrigger
Wraps Radix Tabs.Trigger. Renders role="tab".
Props
TabsContent
Wraps Radix Tabs.Content. Renders role="tabpanel"; only the panel whose value matches the active tab is shown.