Tabs
Shows one of several panels in the same place. Use it where content is switched between rather than laid out side by side.
import { Tab, TabPanel, Tabs } from 'neba';
<Tabs defaultValue="overview">
<Tab value="overview">Overview</Tab>
<Tab value="usage">Usage</Tab>
<TabPanel value="overview">Three deploys today, all green.</TabPanel>
<TabPanel value="usage">1,284 build minutes used.</TabPanel>
</Tabs>;There is no <TabList> wrapper. Write Tabs and TabPanels side by side and the component sorts them into the bar and the panel area.
Props
Tabs
| Prop | Type | Default | Description |
|---|---|---|---|
| variantshared | 'solid' | 'outline' | 'text' | 'outline' | Weight of the tab **bar**, not of the panels. solid is a segmented control, outline is a rule along the bar, text is the same without it |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The tabs' height and type scale, on Button's own ladder, so a tab bar keeps its baseline in a toolbar |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. Arbitrary colour values are not accepted |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| value | string | number | null | — | The chosen tab, for a controlled set |
| defaultValue | string | number | null | — | Which starts chosen |
| onValueChange | (value: string | number | null) => void | — | Called when the chosen tab changes |
| orientationshared | 'horizontal' | 'vertical' | 'horizontal' | Which way the bar runs. vertical stands the tabs down the side and moves the arrow keys onto the other axis |
| activateOnFocus | boolean | false | Whether the arrow keys also choose. Off by default: the moment one panel fetches, walking past four tabs fires four requests |
| loopFocus | boolean | true | Whether the arrow keys wrap from the last tab back to the first |
| overflow | 'scroll' | 'wrap' | 'scroll' | What a bar with more tabs than room does. scroll stays one line and fades the end that has more past it; wrap takes as many lines as it needs |
| lines | number | — | The most tab-rows the bar may be tall; past it the bar scrolls. Only read when overflow is wrap |
| wheel | boolean | true | Turns a wheel rolled over the bar into travel along it, and holds it at the ends too. A bar that fits takes nothing; the pointer leaving the bar is what gives the page its wheel back |
| fullWidth | boolean | false | The tabs share the full width of the bar, each taking an equal part |
| children | ReactNode | — | The Tabs and the TabPanels. They are sorted into their two boxes for you, so there is no list wrapper to remember |
Tab
| Prop | Type | Default | Description |
|---|---|---|---|
| value * | string | number | — | Identifies the tab, and picks out the panel with the same value |
| startIcon | ReactNode | — | Content before the label |
| endIcon | ReactNode | — | Content after the label: a count, a Badge, a status dot |
| disabled | boolean | false | Unavailable, but still listed |
| children | ReactNode | — | The tab's label |
TabPanel
| Prop | Type | Default | Description |
|---|---|---|---|
| value * | string | number | — | Which tab shows this panel |
| keepMounted | boolean | false | Keeps the panel in the DOM while hidden. For content that is expensive to build, or form state that should survive |
| children | ReactNode | — | The content |
Examples
variant
variant is the weight of the tab bar, not of the panels under it.
solid: a filled tile moves between the tabs inside a trough.outline: the indicator rides on a rule along the edge of the bar.text: the indicator with no rule, for tabs inside a Card that already has an edge.
orientation
vertical puts the bar down the left side. The arrow keys move onto the vertical axis with it.
overflow and lines
overflow says what a bar with more tabs than room does about it. scroll (the default) keeps the bar on one line and scrolls along it; the ends fade while there is more bar in that direction, and the scrollbar itself is hidden. wrap takes as many lines as the tabs need, and the rule under the chosen tab moves onto the line that tab is on.
lines caps a wrapping bar at that many tab-rows and scrolls past the cap. It is read only when overflow is wrap.
wheel
A mouse has one wheel and it points down the page, which is the one axis a scrolling tab bar does not run along. wheel turns a wheel rolled over the bar into travel along it. It is on by default: the bar has no scroll buttons and its scrollbar is hidden, so a wheel the page answers instead leaves the tabs past the edge reachable only by keyboard.
What it takes it keeps, at the ends as well, so a flick that runs out of bar does not turn into a jump down the article. Moving the pointer off the bar is what gives the page its wheel back. A bar that fits takes nothing, so a page with three tabs on it is unaffected. A trackpad swiping sideways is left to the browser, which scrolls it better.
Pass wheel={false} to turn it off.
startIcon and endIcon
Put an icon or a count before or after the label.
size
The same control heights as Button: a md tab and a md button are both 32px, so a tab bar can sit in a toolbar beside one.
activateOnFocus
Off by default: walking the arrow keys along the bar does not change the panel, and Enter or Space activates. That keeps a panel that fetches from firing a request per tab passed.
keepMounted
Set on a TabPanel, it keeps an unselected panel's React tree alive.
Accessibility
- The whole bar is one tab stop, with the arrow keys and Home/End moving within it (a roving tab index).
- The
tab/tabpanelroles and thearia-controlsbetween them are wired up. - A panel with nothing focusable inside it takes focus itself, so the content stays reachable by keyboard.
- The indicator moves via
left/topandwidth/height, so no label is resampled.