Overview
The Tabs component provides an accessible way to organize content into separate views. Built on React Aria Components with full keyboard navigation and multiple visual variants.
Installation
import {
Tabs,
TabList,
Tab,
TabPanel,
} from "@/components/ui/Tabs";
Anatomy
The Tabs component consists of four sub-components:
- Tabs: Root container managing tab state (uses React Aria’s Tabs)
- TabList: Container for tab buttons (uses React Aria’s TabList)
- Tab: Individual tab button (uses React Aria’s Tab)
- TabPanel: Content panel for each tab (uses React Aria’s TabPanel)
Basic Usage
<Tabs defaultSelectedKey="tab1">
<TabList>
<Tab id="tab1">Overview</Tab>
<Tab id="tab2">Analytics</Tab>
<Tab id="tab3">Settings</Tab>
</TabList>
<TabPanel id="tab1">
<div className="p-4">
<h3>Overview</h3>
<p>Welcome to the overview section.</p>
</div>
</TabPanel>
<TabPanel id="tab2">
<div className="p-4">
<h3>Analytics</h3>
<p>View detailed analytics and metrics.</p>
</div>
</TabPanel>
<TabPanel id="tab3">
<div className="p-4">
<h3>Settings</h3>
<p>Manage your settings here.</p>
</div>
</TabPanel>
</Tabs>
Variants
The Tabs component supports four visual variants:
Default
Standard tabs with underline selection indicator.
<Tabs defaultSelectedKey="tab1">
<TabList variant="default">
<Tab id="tab1">Dashboard</Tab>
<Tab id="tab2">Reports</Tab>
<Tab id="tab3">Settings</Tab>
</TabList>
<TabPanel id="tab1">
<div className="p-4">Dashboard content</div>
</TabPanel>
{/* Additional panels */}
</Tabs>
Soft
Rounded tabs with background highlighting.
<Tabs defaultSelectedKey="tab1">
<TabList variant="soft">
<Tab id="tab1" variant="soft">Profile</Tab>
<Tab id="tab2" variant="soft">Notifications</Tab>
<Tab id="tab3" variant="soft">Security</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
Outline
Tabs with border highlighting when selected.
<Tabs defaultSelectedKey="tab1">
<TabList variant="outline">
<Tab id="tab1" variant="outline">Billing</Tab>
<Tab id="tab2" variant="outline">Domains</Tab>
<Tab id="tab3" variant="outline">Storage</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
Ghost
Minimal styling with subtle background on selection.
<Tabs defaultSelectedKey="tab1">
<TabList variant="ghost">
<Tab id="tab1" variant="ghost">Appearance</Tab>
<Tab id="tab2" variant="ghost">Advanced</Tab>
<Tab id="tab3" variant="ghost">Documentation</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
Sizes
Three size options control height, padding, and text size:
{/* Small */}
<Tabs defaultSelectedKey="tab1">
<TabList variant="soft" size="sm">
<Tab id="tab1" variant="soft" size="sm">Tab 1</Tab>
<Tab id="tab2" variant="soft" size="sm">Tab 2</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
{/* Medium (default) */}
<Tabs defaultSelectedKey="tab1">
<TabList variant="soft" size="md">
<Tab id="tab1" variant="soft" size="md">Tab 1</Tab>
<Tab id="tab2" variant="soft" size="md">Tab 2</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
{/* Large */}
<Tabs defaultSelectedKey="tab1">
<TabList variant="soft" size="lg">
<Tab id="tab1" variant="soft" size="lg">Tab 1</Tab>
<Tab id="tab2" variant="soft" size="lg">Tab 2</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
Props
Tabs
orientation
string
default:"horizontal"
Layout orientation: "horizontal" | "vertical"
Default selected tab (uncontrolled)
Selected tab (controlled)
Callback when selection changes
Additional CSS classes to apply
TabList
Visual style: "default" | "soft" | "outline" | "ghost"
orientation
string
default:"horizontal"
Layout orientation: "horizontal" | "vertical"
Size of tabs: "sm" | "md" | "lg"
Additional CSS classes to apply
Tab
Unique identifier matching TabPanel id
Visual style (should match TabList variant)
Size (should match TabList size)
orientation
string
default:"horizontal"
Orientation (should match Tabs orientation)
Disable this specific tab
Additional CSS classes to apply
TabPanel
Unique identifier matching Tab id
orientation
string
default:"horizontal"
Orientation (should match Tabs orientation)
Additional CSS classes to apply
Vertical Orientation
Tabs can be displayed vertically for sidebar-style navigation:
import { User, Settings, Bell, Shield } from "lucide-react";
<Tabs orientation="vertical" defaultSelectedKey="profile">
<TabList variant="soft" orientation="vertical">
<Tab id="profile" variant="soft" orientation="vertical">
<User size={16} className="mr-2" />
Profile
</Tab>
<Tab id="account" variant="soft" orientation="vertical">
<Settings size={16} className="mr-2" />
Account
</Tab>
<Tab id="notifications" variant="soft" orientation="vertical">
<Bell size={16} className="mr-2" />
Notifications
</Tab>
<Tab id="security" variant="soft" orientation="vertical">
<Shield size={16} className="mr-2" />
Security
</Tab>
</TabList>
<TabPanel id="profile" orientation="vertical">
<div className="p-6">Profile settings content</div>
</TabPanel>
{/* Additional panels */}
</Tabs>
Tabs with Icons
Enhance tabs with icons for better visual navigation:
import { BarChart3, FileText, Settings } from "lucide-react";
<Tabs defaultSelectedKey="dashboard">
<TabList variant="outline">
<Tab id="dashboard" variant="outline">
<BarChart3 size={18} className="mr-2" />
Dashboard
</Tab>
<Tab id="reports" variant="outline">
<FileText size={18} className="mr-2" />
Reports
</Tab>
<Tab id="settings" variant="outline">
<Settings size={18} className="mr-2" />
Settings
</Tab>
</TabList>
<TabPanel id="dashboard">
<div className="p-6">Dashboard content with charts</div>
</TabPanel>
{/* Additional panels */}
</Tabs>
Disabled Tabs
Disable individual tabs or all tabs:
<Tabs defaultSelectedKey="tab1">
<TabList>
<Tab id="tab1">Active Tab</Tab>
<Tab id="tab2" isDisabled>Disabled Tab</Tab>
<Tab id="tab3">Another Active Tab</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
Controlled Tabs
Manage tab selection state externally:
const [selectedTab, setSelectedTab] = useState("tab1");
<Tabs selectedKey={selectedTab} onSelectionChange={setSelectedTab}>
<TabList>
<Tab id="tab1">Tab 1</Tab>
<Tab id="tab2">Tab 2</Tab>
<Tab id="tab3">Tab 3</Tab>
</TabList>
{/* TabPanels */}
</Tabs>
React Aria Components Integration
The Tabs component uses React Aria Components for:
State Management
- Single source of truth for selected tab
- Automatic focus management
- Keyboard navigation handling
Accessibility
- Proper ARIA roles and attributes
- Keyboard navigation support
- Screen reader announcements
Features
- Automatic TabPanel mounting/unmounting
- Focus restoration when tabs change
- RTL (right-to-left) support
Accessibility Features
Keyboard Navigation
Horizontal Orientation:
- Tab: Move focus into/out of tab list
- Left Arrow: Select previous tab
- Right Arrow: Select next tab
- Home: Select first tab
- End: Select last tab
Vertical Orientation:
- Tab: Move focus into/out of tab list
- Up Arrow: Select previous tab
- Down Arrow: Select next tab
- Home: Select first tab
- End: Select last tab
ARIA Attributes
role="tablist" on TabList
role="tab" on Tab buttons
role="tabpanel" on TabPanel
aria-selected indicates active tab
aria-controls links tabs to panels
aria-labelledby associates panels with tabs
Screen Reader Support
- Tab selection changes are announced
- Disabled tabs are identified
- Current tab position is communicated
Design Tokens
The Tabs component uses semantic design tokens:
--font-family-primary: Typography
--text-primary/secondary/tertiary: Text colors
--bg-primary/secondary/tertiary: Backgrounds
--border-primary/secondary/focus: Borders
--interactive-primary: Active state color
--transition-normal: Animation duration
Best Practices
- Consistent variant: Use the same variant for TabList and Tab components
- Meaningful labels: Use clear, concise labels that describe panel content
- Appropriate count: Limit to 5-7 tabs for horizontal layouts
- Default selection: Always set a default selected tab
- Loading states: Show loading indicators when tab content is async
- Preserve state: Consider preserving panel state when switching tabs
- Mobile considerations: Use vertical orientation or responsive design for mobile
Advanced Example: Settings Interface
import { User, Bell, Shield, CreditCard } from "lucide-react";
<Tabs defaultSelectedKey="profile" orientation="vertical">
<TabList variant="soft" orientation="vertical">
<Tab id="profile" variant="soft" orientation="vertical">
<User size={16} className="mr-2" />
Profile
</Tab>
<Tab id="notifications" variant="soft" orientation="vertical">
<Bell size={16} className="mr-2" />
Notifications
</Tab>
<Tab id="security" variant="soft" orientation="vertical">
<Shield size={16} className="mr-2" />
Security
</Tab>
<Tab id="billing" variant="soft" orientation="vertical">
<CreditCard size={16} className="mr-2" />
Billing
</Tab>
</TabList>
<TabPanel id="profile" orientation="vertical">
<div className="p-6">
<h3 className="text-lg font-semibold mb-4">Profile Settings</h3>
{/* Profile form fields */}
</div>
</TabPanel>
{/* Additional panels */}
</Tabs>
Dark Mode
Tabs automatically adapt to dark mode using semantic tokens:
<Tabs defaultSelectedKey="tab1">
<TabList variant="soft">
<Tab id="tab1" variant="soft">Tab 1</Tab>
<Tab id="tab2" variant="soft">Tab 2</Tab>
</TabList>
{/* Content adapts to theme automatically */}
</Tabs>