Tabs
Preview appears after the next Storybook update.
<Tabs />When to use
Use it for
- Two to seven peer views of the same context, where users switch back and forth.
- underline for page and card sections; contained for a compact switch inside a toolbar (Light or Dark preview).
- count to show how many items wait in a view, like open reviews.
- Vertical orientation for settings pages with many sections next to their content.
Not for
- Steps of a sequence the user must complete in order: use a stepper or a form with sections.
- Navigation between pages with their own URL: use SidebarNav or links styled as tabs, not Tabs (which do not change the URL).
- Changing a setting or filter value: use RadioGroup or an attached ButtonGroup.
Do and don't
Every example is rendered with the real component, so it always matches what ships.
Short, parallel labels
One or two nouns per tab, same grammatical form. The user scans them as a set.
Tabs that are really actions
"Export" or "Delete" in a tab list hides an action behind a view switch. Use a Button.
Accessibility
- Base UI Tabs: role tablist, tab and tabpanel, aria-selected, aria-controls and aria-labelledby are wired for you.
- One tab stop for the whole list. Arrow keys move between tabs (Up and Down when vertical), Home and End jump, focus wraps by default.
- By default arrows move focus and Enter or Space selects; set activateOnFocus when panels are cheap to render.
- Give the TabsList an aria-label (or labelledby) so the tablist has a name.
- The selected tab has a visible signal underline plus stronger text: never color alone.
- Disabled tabs are announced as disabled and cannot be selected.
Content guidelines
- Sentence case nouns: "Tokens", "Open reviews".
- No verbs, no punctuation, no more than two or three words.
- Counts are numbers in the count prop, not in the label text.
From the code Generated from the component, not editable here.
Anatomy and tokens
Read from packages/react/src/components/Tabs/Tabs.module.css at build time. Every value is a token: open one to see its alias chain in both themes.
- Container
.root - Tab list
.list - Tab list
.list[data-overflow-end] - Tab list
.list[data-overflow-start] - Tab list
.list[data-overflow-start][data-overflow-end] - sm
.sm - Tab button
.tab - Tab button, hover
@media (hover: hover) .tab:hover:not([data-disabled], [data-active]) - Tab button
.tab[data-active] - Tab button, focus
.tab:focus-visible - Tab button
.tab[data-disabled] - Count
.count - Tab button
.tab[data-active] .count - Underline variant
.underline - Underline variant
.underline::after - Underline variant
.underline .tab - Underline variant
.underline[data-orientation="vertical"]::after - Underline variant
.underline[data-orientation="vertical"] .tab - Selection indicator (signal underline or raised plate)
.indicator - Underline variant
.underline .indicator - Underline variant
.underline[data-orientation="vertical"] .indicator - Contained variant
.contained - Contained variant
.contained .tab - Contained variant
.contained.sm .tab - Contained variant
.contained .indicator - Panel
.panel - Panel, focus
.panel:focus-visible
Motion and interaction
Tabs animates on hover. Try it in the preview at the top.
| When | What changes | Duration | Easing |
|---|---|---|---|
| Hover | Text color | motion.duration.instant75 ms | motion.easing.standardsettles in place |
With reduced motion turned on, every change happens at once.
It also uses motion.duration.moderate for motion this table does not list yet.
From the code Generated from the component, not editable here.
Props
Parsed from the TypeScript types in packages/react/src/components/Tabs/Tabs.tsx, so this table cannot drift from the code. It also accepts every native <div> attribute except defaultValue, onChange.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'underline' | 'contained' | 'underline' | `underline` for page-level sections (signal underline); `contained` for a compact segmented switch. Default `underline`. |
size | 'sm' | 'md' | 'md' | Tab height follows density tokens. Default `md`. |
defaultValue | string | number | null | none | Value of the tab selected on first render (uncontrolled). |
value | string | number | null | none | Value of the selected tab (controlled). |
onValueChange | (value: TabsValue) => void | none | Called with the new value when the user selects a tab. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | No description in the code yet. |
children | ReactNode | none | No description in the code yet. |
Comes with watchOverflowEdges, each accepting native <div> attributes.
Playground
Change the props and see the real Tabs update. Controls are generated from the TypeScript props; nothing here changes the system.
<Tabs defaultValue="tokens" />Workbench
For contributors: every state with the accessibility panel, in the component workbench (Storybook). The stories double as visual test fixtures; the guidance on this page is the user documentation (open Tabs in Storybook).
Embed Storybook stories here (4)
Loads from https://bridge-storybook.floriangouloubi.com (set STORYBOOK_URL at build time). If Storybook is not running, the frames stay empty.
Underline
Contained
Vertical
AllStates
From the code Generated from the component, not editable here.
Changelog
- New
Navigation: Tabs (underline with a sliding signal indicator, contained, icons, counts, vertical), Breadcrumb (aria-current, collapsing middle levels), Pagination (full and compact, getPageItems helper) and SidebarNav (sections, collapsible groups, counts, current page mark)
v1.0.0, Nov 11, 2026
- New
Being built gets the two filters of Changes: All products and Everyone (with You) on the line under the tabs, then the count, in the same ?product= and ?people= addresses
v1.0.0, Nov 11, 2026
- New
Studio Changes: the tabs say where the work stands (All, Waiting on me, Not started, Exploring, Draft, In review, Waiting for code, Archived, each with its count; empty state tabs hide)
v1.0.0, Nov 11, 2026
- New
Studio Branches is all the work: the summary banner, the tabs and the status menu are gone
v1.0.0, Nov 11, 2026
- Fix
Being built: the filters are DS Tabs (underline) with a count on each, like Changes
v1.0.0, Nov 11, 2026
- Fix
Studio Changes: the state filter is DS Tabs (underline) instead of a segmented control, since it holds up to eight states with counts
v1.0.0, Nov 11, 2026