Switch between related views in the same context.
Tabs
The Tabs component lets users switch between related views without leaving the page. Built on Radix UI's tabs primitive, it handles keyboard navigation and ARIA attributes correctly.
Import#
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@aidimension/ui/components/tabs";Basic usage#
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
<TabsTrigger value="reports">Reports</TabsTrigger>
</TabsList>
<TabsContent value="overview">Overview content</TabsContent>
<TabsContent value="analytics">Analytics content</TabsContent>
<TabsContent value="reports">Reports content</TabsContent>
</Tabs>Controlled#
const [tab, setTab] = useState("overview");
<Tabs value={tab} onValueChange={setTab}>
{/* ... */}
</Tabs>Vertical orientation#
<Tabs defaultValue="profile" orientation="vertical">
<TabsList className="flex-col h-auto w-48">
<TabsTrigger value="profile" className="w-full">Profile</TabsTrigger>
<TabsTrigger value="settings" className="w-full">Settings</TabsTrigger>
</TabsList>
<TabsContent value="profile">Profile form</TabsContent>
<TabsContent value="settings">Settings form</TabsContent>
</Tabs>With icons#
import { User, Settings, Bell } from "lucide-react";
<Tabs defaultValue="profile">
<TabsList>
<TabsTrigger value="profile" className="gap-2">
<User className="size-4" /> Profile
</TabsTrigger>
<TabsTrigger value="settings" className="gap-2">
<Settings className="size-4" /> Settings
</TabsTrigger>
<TabsTrigger value="notifications" className="gap-2">
<Bell className="size-4" /> Notifications
</TabsTrigger>
</TabsList>
{/* contents... */}
</Tabs>API reference#
| Component | Description |
|---|---|
Tabs | Root, manages selected tab. |
TabsList | Container for triggers. |
TabsTrigger | Button that activates a tab. |
TabsContent | Panel shown when its tab is active. |
Tabs props#
| Prop | Type | Default | Description | |
|---|---|---|---|---|
value | string | — | Controlled value | |
defaultValue | string | — | Initial uncontrolled value | |
onValueChange | (value: string) => void | — | Change handler | |
orientation | `"horizontal" \ | "vertical"` | "horizontal" | Layout direction |
activationMode | `"automatic" \ | "manual"` | "automatic" | When to activate (hover vs click) |
Accessibility#
- ✅
role="tablist",role="tab",role="tabpanel"set automatically - ✅ Arrow keys move between tabs
- ✅
Home/Endjump to first / last tab - ✅
Tabkey moves focus into the active panel - ✅
aria-selectedandaria-controlsset correctly
Common patterns#
Settings page#
<Tabs defaultValue="account" className="w-full max-w-2xl">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
</TabsList>
<TabsContent value="account">
<Card>
<CardHeader>
<CardTitle>Account</CardTitle>
<CardDescription>Update your account info.</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<Input placeholder="Name" />
<Input placeholder="Email" type="email" />
<Button>Save</Button>
</CardContent>
</Card>
</TabsContent>
{/* ... */}
</Tabs>Code preview#
<Tabs defaultValue="preview">
<TabsList>
<TabsTrigger value="preview">Preview</TabsTrigger>
<TabsTrigger value="code">Code</TabsTrigger>
</TabsList>
<TabsContent value="preview">
<div className="rounded-lg border p-8"><MyComponent /></div>
</TabsContent>
<TabsContent value="code">
<pre><code>{sourceCode}</code></pre>
</TabsContent>
</Tabs>