Tabs
Switch between panels in the same context. Full WAI-ARIA keyboard support: arrows rove, Home and End jump, and only the active tab is in the tab order.
src/components/ui/tabs.tsxInstall
npx astralyx-ui add tabs
Usage
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="activity">Activity</TabsTrigger>
</TabsList>
<TabsContent value="overview">…</TabsContent>
<TabsContent value="activity">…</TabsContent>
</Tabs>Composer
Change the props and the source below follows.
Everything at a glance.
Props
tsx
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="activity">Activity</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="overview">…</TabsContent>
</Tabs>Examples
A tab group
tsx
<Tabs defaultValue="dashboard" variant="browser">
<TabsList className="items-end">
<GroupChip>Work</GroupChip>
<TabsTrigger value="dashboard">Dashboard</TabsTrigger>
<TabsTrigger value="issues">Issues · 12</TabsTrigger>
<TabsTrigger value="docs">Docs</TabsTrigger>
</TabsList>
<TabsContent value="dashboard">…</TabsContent>
</Tabs>Browser
tsx
<Tabs defaultValue="overview" variant="browser">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
</TabsList>
<TabsContent value="overview">…</TabsContent>
</Tabs>Solid
tsx
<Tabs defaultValue="overview"> <TabsList>…</TabsList> </Tabs>
Underline
tsx
<TabsList variant="underline"> <TabsTrigger value="overview" variant="underline">Overview</TabsTrigger> </TabsList>
Vertical
tsx
<Tabs defaultValue="overview" orientation="vertical">…</Tabs>
Disabled trigger
tsx
<TabsTrigger value="settings" disabled>Settings</TabsTrigger>
API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
Tabs
Extends Omit<ComponentProps<'div'>, 'onChange'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultValue | string | number | readonly string[] | — | Controlled and uncontrolled selection, on Tabs. |
| variant | TabsVariant | 'solid' | Set it once on Tabs and the list, the triggers and the panel all follow. Setting it on a child still wins, which is how it was written before the root took the prop. |
| size | SegmentSize | 'default' | The Button's size scale. A solid track takes the Button's height and corner, so a tab list in a toolbar lines up with the Buttons and Inputs of the same size; every variant takes its label and icon size. Set on Tabs or TabsList and the triggers inherit it; a size on a trigger still wins. |
| value | string | — | Controlled and uncontrolled selection, on Tabs. |
| onValueChange | (value: string) => void | — | Fires with the newly selected tab. |
| orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction, and which arrow keys move between tabs. |
| activationMode | 'automatic' | 'manual' | 'automatic' | Automatic selects as focus moves; manual waits for Enter or Space. |
TabsList
Extends ComponentProps<'div'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'solid' | 'underline' | 'browser' | 'solid' | Set it once on Tabs and the list, the triggers and the panel all follow. Setting it on a child still wins, which is how it was written before the root took the prop. |
| size | 'sm' | 'default' | 'xs' | 'lg' | 'xl' | 'default' | The Button's size scale. A solid track takes the Button's height and corner, so a tab list in a toolbar lines up with the Buttons and Inputs of the same size; every variant takes its label and icon size. Set on Tabs or TabsList and the triggers inherit it; a size on a trigger still wins. |
| orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction, and which arrow keys move between tabs. |
TabsTrigger
Extends Omit<ComponentProps<'button'>, 'value'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'solid' | 'underline' | 'browser' | 'solid' | Set it once on Tabs and the list, the triggers and the panel all follow. Setting it on a child still wins, which is how it was written before the root took the prop. |
| size | 'sm' | 'default' | 'xs' | 'lg' | 'xl' | 'default' | The Button's size scale. A solid track takes the Button's height and corner, so a tab list in a toolbar lines up with the Buttons and Inputs of the same size; every variant takes its label and icon size. Set on Tabs or TabsList and the triggers inherit it; a size on a trigger still wins. |
| value | string | required | Required. Matches the TabsContent it controls. |
TabsContent
A panel.
Extends ComponentProps<'div'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | required | Controlled and uncontrolled selection, on Tabs. |
| keepMounted | boolean | false | — |
| variant | TabsVariant | 'solid' | Set it once on Tabs and the list, the triggers and the panel all follow. Setting it on a child still wins, which is how it was written before the root took the prop. |
Functions
| Function | Signature | Description |
|---|---|---|
| listVariants | (props?: { variant?: 'solid' | 'underline' | 'browser'; orientation?: 'horizontal' | 'vertical'; size?: 'sm' | 'default' | 'xs' | 'lg' | 'xl' }) => string | — |
| triggerVariants | (props?: { variant?: 'solid' | 'underline' | 'browser'; size?: 'sm' | 'default' | 'xs' | 'lg' | 'xl' }) => string | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| browser | — | The connected look: the active tab and the panel are one fill with nothing drawn between them, and two masked flares carve the strip away at the bottom corners. No borders anywhere — a bordered version has to hide one border with another and comes apart at every zoom level. |
| transition | — | The panel cross-fades on switch and does not move. Travel on a panel root means a transform, and a transform is a containing block for any fixed layer inside it — a popover opened from a tab would land in the wrong place. The tab shape morphs instead, flares included. |