Arrow keys move between tabs, Home and End jump to the first and last, and focusing a tab selects it (automatic activation). Disabled tabs are skipped. The panels use the tabpanel role and are focusable. Each TabsTrigger and TabsContent pair must share a value. Tabs is horizontal only.
TabsList keeps the width of its tabs (it never stretches across the column) and, when the tabs do not fit, scrolls sideways on its own with the scrollbar hidden. The selected tab is scrolled into view on mount and whenever it changes. The leading icon snippet and brand logos are covered on the Dropdown Menu page too (same icon / brandIcon pair). Add fade to mask the edge that still has tabs to reveal. No wrapper element or CSS is needed for 7 tabs on a phone.
variant="line" drops the glass pill: bare text tabs with a hairline under the strip and a 2px underline on the active tab, the usual look in a page header. Tabs that only switch a view (no TabsContent) are fine: a trigger points aria-controls at its panel only while a TabsContent with the same value is mounted, so nothing references a missing element. Name the strip with aria-label (or aria-labelledby) on TabsList; TabsList and TabsTrigger forward any other attribute (data-*, id, aria-*, event handlers) to their element.
TabsTrigger forwards any other button attribute (data-testid, id, aria-*, onclick, onfocus) to its <button role="tab">, and TabsList forwards data-*, id and aria-* to the role="tablist" element. role, type and the roving tabindex stay owned by the component.
#164
Default
Three tabs with content panels.
The Tabs component implements the WAI-ARIA tabs pattern. Arrow keys cycle through tabs, Home and End jump to the first and last.
Tabs accepts a value prop for controlled mode and onchange for change notifications. Uncontrolled mode works too.
Common uses: settings pages, detail views with several data categories, code and preview panes, and stepped forms.
Fires when the active tab changes by click or keyboard.
variant
'pill' | 'line'
'pill'
`pill` fills the active tab on a glass list. `line` is bare text with an underline on the active tab, for page headers.
children
Snippet
–
`TabsList` and `TabsContent` children.
class
string
''
Extra classes on the root.
TabsTrigger
Prop
Type
Default
Description
value
string
–
Required. Links the trigger to its TabsContent.
disabled
boolean
false
Prevents selecting the tab.
icon
Snippet
–
Leading icon in a fixed 14px box, `aria-hidden`: the label stays the accessible name. Any `svg` or `img` inside the box fills it, including one wrapped in `SchemeAsset`. A plain icon follows the tab's text color.
brandIcon
boolean
false
The icon is a brand mark with its own colors: no active, hover or disabled color reaches it. Pair it with `SchemeAsset` when the mark needs a light and a dark asset.
children
Snippet
–
Trigger label.
class
string
''
Extra CSS classes.
TabsContent
Prop
Type
Default
Description
value
string
–
Required. Shown while the matching trigger is active.
children
Snippet
–
Panel content.
class
string
''
Extra CSS classes.
TabsList
Prop
Type
Default
Description
fade
boolean
false
Fade the edge(s) that still hide tabs, so the strip reads as scrollable.
aria-label
string
–
Accessible name of the tablist (also `aria-labelledby`). Name it whenever the page has more than one tablist or the purpose is not obvious.