Tabs
Switch between peer views inside one screen: Tabs, TabList, Tab, and TabPanel. Six types across two orientations, no scrolling of their own.
Tabs
Switch between peer views inside one screen: Tabs, TabList, Tab, and TabPanel. Six types across two orientations, no scrolling of their own.
Demo
The controls sit on TabList, where the kit puts every visual decision — except orientation, which lives on the root. Flip it to vertical and underline gives way to line.
Three components, one bar
Tabs owns the selection, Tabs.List owns the look, and Tabs.Item owns one label. The copied snippet carries all three, because no single element reproduces what the preview shows.Contextual demo
Tabs only make sense next to the thing they switch: four bars in a real screen, each wired to real panels.
This page is also a consumer
type="button-brand" at size="sm", rendered by the docs page shell. The three tabs are the panels, and ?tab= in the address bar is the selected key.The family
Four exports, also reachable as Tabs.List, Tabs.Item, and Tabs.Panel off the root.
| Component | Description | Import from |
|---|---|---|
| Tabs | The root. Owns the selected key, the orientation, and the keyboard model, and lays the list and the panels out in a column horizontally, a row vertically. | @/components/application/tabs/tabs |
| TabList | The bar itself: every visual decision lives here, since size, type, and fullWidth are its props rather than the root’s. | @/components/application/tabs/tabs |
| Tab | One tab. Takes a label, an optional leading icon, and an optional badge. | @/components/application/tabs/tabs |
| TabPanel | The content for one tab, matched to it by id. It becomes a tab stop of its own when it holds nothing focusable. | @/components/application/tabs/tabs |
Built from
Four pieces make up the bar; one has its own docs page.
Component | Where it’s used here |
|---|---|
| Badge | The badge prop, at size sm, as pill-color on the brand, underline, and line types and modern everywhere else. On those three the color is brand only while the tab is selected or hovered and gray the rest of the time; the modern badge is always gray. |
The variant space
Five horizontal types at both sizes — the ten Desktop bars the sheet draws — all live. Wide bars scroll in narrow cells.
Size moves everything at once
sm and 44px at md, an underline tab 32px then 36px, with the label at 14px then 16px and the icon at 16px then 20px. Every one of those matches the sheet symbol beside it.Vertical
The same five types at both sizes with underline swapped for line — the ten vertical Desktop rails the sheet draws. Underline belongs to a horizontal bar, line to a vertical one.
Orientation belongs on Tabs
TabList no longer takes an orientation prop (deprecated 2026-08-21, removed 2026-08-22): it only ever styled the list. Arrow-key direction, the data-orientation attribute and the list’s own layout all come from the root, so set it on Tabs and the list follows. The trade-off is that the type union no longer narrows, so the compiler will let a vertical bar take underline. Pair them yourself.States
Selection, hover, and focus are the sheet’s three. Disabled is React Aria’s fourth, painted only as an opacity change.
A disabled tab is dimmed, not explained
getTabStyles reads isDisabled and paints it at half opacity with a not-allowed cursor, or in the system’s GrayText under forced colors. It also stays flat on hover, since React Aria passes isDisabled into useHover, so isHovered never turns true while its neighbors light up under the pointer. The opacity tells a reader something is off; it does not say what. The sheet draws no disabled symbol either way.