Tabs
Switches between panels of related content.
Two visual treatments. The variant is read from context rather than taken as a prop on the list, so the root and the list can never disagree.
Import
import {
Tabs,
TabsList,
TabsTrigger,
TabsPanel,
} from "@abbainitiative/ui";Also available from the subpath entry @abbainitiative/ui/tabs.
This component carries its own "use client" directive. You can still render it from a Server Component — you simply cannot pass it a function prop, because functions do not serialise across the boundary.
Examples
Line and enclosed
The line variant, which is the default.
The enclosed variant.
<Tabs defaultValue="overview">
<TabsList aria-label="Project sections">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="activity">Activity</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsPanel value="overview">Overview content</TabsPanel>
<TabsPanel value="activity">Activity content</TabsPanel>
<TabsPanel value="settings">Settings content</TabsPanel>
</Tabs>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "line" | "enclosed" | "line" | Visual treatment of the tab list. |
| defaultValue | string | Initially active tab when uncontrolled. | |
| value | string | Controlled active tab. | |
| onValueChange | (value: string) => void | Called when the active tab changes. |
Accessibility
- Arrow keys move between tabs; Tab enters the list once and then moves to the panel (roving tabindex).
- Each tab is wired to its panel with `aria-controls` and `aria-labelledby`.
- Give TabsList an `aria-label` so the tab set is identifiable.
- Many tabs scroll horizontally on narrow viewports rather than wrapping into a broken grid.