Tabs
A set of layered panels where only one panel is displayed at a time, triggered by corresponding tab buttons.
Overview
The Tabs component provides an accessible way to organize content into switchable panels. It supports two visual variants, an optional animated indicator, vertical orientation, and controlled or uncontrolled state. Built on Base UI's Tabs primitive.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@rogo-technologies/ui/tabs"
<Tabs defaultValue="account">
<TabsList variant="primary">
<TabsTrigger variant="primary" value="account">Account</TabsTrigger>
<TabsTrigger variant="primary" value="security">Security</TabsTrigger>
</TabsList>
<TabsContent value="account">Account settings here.</TabsContent>
<TabsContent value="security">Security settings here.</TabsContent>
</Tabs>Primary Variant
The default variant renders an underline-style tab bar with a bottom border. Active tabs get the brand color border.
Manage your account settings and preferences.
<Tabs defaultValue="account">
<TabsList variant="primary">
<TabsTrigger variant="primary" value="account">Account</TabsTrigger>
<TabsTrigger variant="primary" value="security">Security</TabsTrigger>
<TabsTrigger variant="primary" value="notifications">Notifications</TabsTrigger>
</TabsList>
<TabsContent value="account">...</TabsContent>
<TabsContent value="security">...</TabsContent>
<TabsContent value="notifications">...</TabsContent>
</Tabs>Secondary Variant
The secondary variant renders pill-style tabs inside a rounded container. Active tabs get a raised background.
Showing all items.
<Tabs defaultValue="all">
<TabsList variant="secondary">
<TabsTrigger variant="secondary" value="all">All</TabsTrigger>
<TabsTrigger variant="secondary" value="active">Active</TabsTrigger>
<TabsTrigger variant="secondary" value="archived">Archived</TabsTrigger>
</TabsList>
<TabsContent value="all">...</TabsContent>
</Tabs>Animated Indicator
Pass the same activeLayoutId string to each trigger to enable a smooth animated underline that slides between tabs using framer-motion layoutId.
Dashboard overview with key metrics.
<Tabs defaultValue="overview">
<TabsList variant="primary">
<TabsTrigger variant="primary" value="overview" activeLayoutId="my-indicator">
Overview
</TabsTrigger>
<TabsTrigger variant="primary" value="analytics" activeLayoutId="my-indicator">
Analytics
</TabsTrigger>
</TabsList>
<TabsContent value="overview">...</TabsContent>
<TabsContent value="analytics">...</TabsContent>
</Tabs>Disabled
Individual tabs can be disabled using the disabled prop.
This tab is active.
<TabsTrigger variant="primary" value="disabled" disabled>
Disabled
</TabsTrigger>Vertical
Use orientation="vertical" on the root and adjust the list styling for a vertical tab layout. Override classNames on TabsList and TabsTrigger to switch the border from bottom to right.
General settings.
<Tabs defaultValue="general" orientation="vertical">
<TabsList
variant="primary"
className="flex-col border-b-0 border-r border-primary h-auto w-48 gap-0"
>
<TabsTrigger
variant="primary"
value="general"
className="w-full justify-start border-b-0 border-r-2 px-4 py-2"
>
General
</TabsTrigger>
{/* ... */}
</TabsList>
<TabsContent value="general" className="mt-0">...</TabsContent>
</Tabs>Interactive
A fully controlled example with external state management.
Content for the first tab.
const [activeTab, setActiveTab] = useState("tab1")
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList variant="primary">
<TabsTrigger variant="primary" value="tab1">First</TabsTrigger>
<TabsTrigger variant="primary" value="tab2">Second</TabsTrigger>
</TabsList>
<TabsContent value="tab1">...</TabsContent>
<TabsContent value="tab2">...</TabsContent>
</Tabs>Props
Tabs
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled active tab value. |
defaultValue | string | — | Uncontrolled initial active tab. |
onValueChange | (value: string) => void | — | Callback when the active tab changes. |
orientation | "horizontal" | "vertical" | "horizontal" | Layout direction. |
className | string | — | Additional CSS classes. |
TabsList
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "primary" | Visual style variant. |
className | string | — | Additional CSS classes. |
TabsTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Unique tab identifier (required). |
variant | "primary" | "secondary" | "primary" | Visual style variant. |
activeLayoutId | string | — | When set, renders an animated indicator using framer-motion layoutId. Primary variant only. |
disabled | boolean | false | Disables the tab. |
className | string | — | Additional CSS classes. |
TabsContent
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Value matching a TabsTrigger (required). |
keepMounted | boolean | false | Keep DOM element when panel is hidden. |
className | string | — | Additional CSS classes. |
Usage Guidelines
Do
- Match
variantonTabsListandTabsTriggerfor consistent styling - Use
activeLayoutIdwith the same string on all triggers when you want a sliding indicator - Use
orientation="vertical"for settings sidebars and navigation panels - Keep tab labels short — one or two words
Don't
- Don't nest tabs inside tabs — it creates confusing navigation
- Don't use tabs for sequential steps — use a stepper or wizard instead
- Don't use more than 5–6 tabs — group or collapse content if needed
- Don't mix variants within the same tab group