Layered panels, one shown at a time, chosen from a list of tabs.
Example
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@elements/ui/tabs";
<Tabs value="account">
<TabsList label="Settings">
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">Make changes to your account here.</TabsContent>
<TabsContent value="password">Change your password here.</TabsContent>
</Tabs>
Parts
| Part |
What it is |
Tabs |
The root. Owns value, the active tab. |
TabsList |
The tablist: one tab stop, arrows between its triggers. |
TabsTrigger |
A tab button. Activates the panel with the same value. |
TabsContent |
A panel. Shown while its value is active, hidden otherwise. |
Attributes
Tabs
| Attribute |
Type |
Default |
Description |
value |
string | null (two-way) |
"" |
The active tab. Empty selects the first enabled tab, already in the server HTML. null starts with no tab active and no panel. |
orientation |
"horizontal" | "vertical" |
"horizontal" |
Lays the list out in a row or a column and picks the arrow keys. |
activation |
"automatic" | "manual" |
"automatic" |
automatic activates a tab when it gets focus; manual waits for Enter or Space. |
dir |
"" | "ltr" | "rtl" |
"" |
Reading direction. Empty inherits. Flips Left and Right. |
loop |
boolean |
true |
Arrow keys wrap from the last tab to the first. |
variant |
"line" | "segmented" |
"line" |
line underlines the active tab; segmented raises it on a muted track. |
onValueChange |
(value: string, details: DisclosureDetails) => void |
() => {} |
Called when the user activates a tab, before the switch, with details second (reason, event, trigger, cancel(), isCanceled; reason none). details.cancel() keeps the current tab, for an unsaved-changes guard. Not called for changes the caller makes. |
onTabDelete |
(value: string) => void |
undefined |
When set, Delete on a focused tab calls it so the caller can remove the tab; focus moves to the next tab. |
class |
string |
"" |
Extra classes on the root. |
TabsList
| Attribute |
Type |
Default |
Description |
label |
string |
"" |
aria-label for the tablist. |
attrs |
Record<string, string | number | boolean | undefined> |
{} |
Other attributes for the tablist, such as aria-labelledby for a visible label. TabsContent takes attrs too. |
class |
string |
"" |
Extra classes on the list. |
TabsTrigger
| Attribute |
Type |
Default |
Description |
value |
string |
(required) |
Pairs the tab with its panel. |
href |
string |
"" |
Renders the tab as a link (a[role=tab]): same tab stop and arrows; following it navigates. |
disabled |
boolean |
false |
Cannot be activated; arrows skip it. |
attrs |
Record<string, string | number | boolean | undefined> |
{} |
Other attributes for the button: id, title, data-*. |
class |
string |
"" |
Extra classes on the tab. |
TabsContent
| Attribute |
Type |
Default |
Description |
value |
string |
(required) |
Pairs the panel with its tab. |
class |
string |
"" |
Extra classes on the panel. |
Slots
| Slot |
Part |
Content |
default |
Tabs |
A TabsList and TabsContents. |
default |
TabsList |
TabsTriggers. |
default |
TabsTrigger |
The label: text, an <Icon>, or both. |
default |
TabsContent |
The panel. |
Keyboard
| Keys |
Action |
Tab |
Into the list, focuses the active tab; from a tab, moves to its panel. |
ArrowRight / ArrowLeft |
Next or previous tab (horizontal, flipped right to left); activates it in automatic mode. |
ArrowDown / ArrowUp |
Next or previous tab (vertical). |
Home / End |
First or last tab. |
Enter / Space |
Activates the focused tab (manual mode). |
Delete |
Closes the focused tab when the root has onTabDelete. |
Data attributes
| Attribute |
Values |
On |
data-orientation |
"horizontal" | "vertical" |
root, list, tab, panel |
data-state |
"active" | "inactive" |
tab, panel |
data-active |
present on the active tab |
tab |
data-index |
the panel's position, from 0 |
panel |
data-hidden |
present while the panel is inactive |
panel |
data-disabled |
present when disabled |
tab |
Accessibility
- Follows the WAI-ARIA tabs pattern: role="tablist" with aria-orientation, role="tab" with aria-selected and aria-controls, role="tabpanel" with aria-labelledby.
- Roving tabindex: the list is one tab stop, on the active tab. Panels are focusable so their text is reachable.
- Inactive panels are hidden and stay mounted, so fields in other tabs keep their values.
- The server renders the active tab and panel, so nothing moves when the page attaches.
See also