Appearance
VerticalTabs
A flexible vertical tab component for side-by-side navigation. Perfect for settings pages, dashboards, and content-heavy interfaces where horizontal space is available.
Overview
VerticalTabs provides a compound component pattern for building vertical tab interfaces. Unlike horizontal tabs that stack content below, vertical tabs place navigation on the left and content on the right, making better use of wide screens.
Key Features
- 📐 Vertical Layout: Side-by-side navigation and content
- 🎨 Rich Content: Support for icons, descriptions, and custom styling
- ✨ Smooth Transitions: Animated content switching
- ♿ Accessible: Full ARIA support and keyboard navigation
- 🎯 Active States: Clear visual feedback for selected tab
- 🔄 Controlled: Optional onChange callback for external state management
When to Use
Use VerticalTabs when:
✅ Settings pages - Multiple configuration sections
✅ Profile pages - Different user information categories
✅ Dashboards - Switching between different views
✅ Wide layouts - Horizontal space is available
✅ Rich navigation - Tabs need icons and descriptions
Use horizontal tabs instead when:
❌ Mobile-first - Limited horizontal space
❌ Simple navigation - Few tabs without descriptions
❌ Narrow layouts - Vertical space is premium
Quick Start
Basic Example
tsx
import {
VerticalTabs,
VerticalTabsList,
VerticalTabTrigger,
VerticalTabContent,
} from '@/shared/ui/components/vertical-tabs/VerticalTabs'
export function SettingsPage() {
return (
<VerticalTabs defaultValue="profile">
<VerticalTabsList>
<VerticalTabTrigger value="profile">
Profile
</VerticalTabTrigger>
<VerticalTabTrigger value="account">
Account
</VerticalTabTrigger>
<VerticalTabTrigger value="security">
Security
</VerticalTabTrigger>
</VerticalTabsList>
<VerticalTabContent value="profile">
<h2>Profile Settings</h2>
{/* Profile form */}
</VerticalTabContent>
<VerticalTabContent value="account">
<h2>Account Settings</h2>
{/* Account form */}
</VerticalTabContent>
<VerticalTabContent value="security">
<h2>Security Settings</h2>
{/* Security form */}
</VerticalTabContent>
</VerticalTabs>
)
}Component Structure
Compound Components
VerticalTabs uses a compound component pattern:
tsx
<VerticalTabs> {/* Root container with state */}
<VerticalTabsList> {/* Left sidebar navigation */}
<VerticalTabTrigger /> {/* Individual tab button */}
<VerticalTabTrigger />
</VerticalTabsList>
<VerticalTabContent /> {/* Right content panel */}
<VerticalTabContent />
</VerticalTabs>Features in Detail
1. With Icons
Add icons to make tabs more recognizable:
tsx
import { User, Settings, Shield, Bell } from 'lucide-react'
<VerticalTabs defaultValue="profile">
<VerticalTabsList>
<VerticalTabTrigger value="profile" icon={<User className="h-4 w-4" />}>
Profile
</VerticalTabTrigger>
<VerticalTabTrigger value="settings" icon={<Settings className="h-4 w-4" />}>
Settings
</VerticalTabTrigger>
<VerticalTabTrigger value="security" icon={<Shield className="h-4 w-4" />}>
Security
</VerticalTabTrigger>
<VerticalTabTrigger value="notifications" icon={<Bell className="h-4 w-4" />}>
Notifications
</VerticalTabTrigger>
</VerticalTabsList>
{/* Content panels */}
</VerticalTabs>2. With Descriptions
Add descriptions for additional context:
tsx
<VerticalTabsList>
<VerticalTabTrigger
value="profile"
icon={<User className="h-4 w-4" />}
description="Manage your personal information"
>
Profile
</VerticalTabTrigger>
<VerticalTabTrigger
value="account"
icon={<Settings className="h-4 w-4" />}
description="Update account preferences"
>
Account
</VerticalTabTrigger>
<VerticalTabTrigger
value="security"
icon={<Shield className="h-4 w-4" />}
description="Password and authentication"
>
Security
</VerticalTabTrigger>
</VerticalTabsList>3. Controlled State
Control the active tab externally:
tsx
function ControlledTabs() {
const [activeTab, setActiveTab] = useState('profile')
return (
<>
<div className="mb-4">
<p>Current tab: {activeTab}</p>
<Button onClick={() => setActiveTab('security')}>
Jump to Security
</Button>
</div>
<VerticalTabs
defaultValue={activeTab}
onChange={(value) => {
setActiveTab(value)
// Track analytics, save to URL, etc.
}}
>
{/* Tabs */}
</VerticalTabs>
</>
)
}4. Custom Styling
Customize the appearance:
tsx
<VerticalTabs defaultValue="profile" className="max-w-5xl mx-auto">
<VerticalTabsList className="w-64 bg-muted/30 rounded-lg p-2">
<VerticalTabTrigger
value="profile"
className="hover:bg-accent"
>
Profile
</VerticalTabTrigger>
</VerticalTabsList>
<VerticalTabContent
value="profile"
className="bg-card rounded-lg p-6"
>
{/* Content */}
</VerticalTabContent>
</VerticalTabs>Common Patterns
Settings Page
tsx
import { User, Lock, Bell, CreditCard, Users } from 'lucide-react'
function UserSettings() {
return (
<div className="container max-w-6xl py-8">
<h1 className="text-3xl font-bold mb-8">Settings</h1>
<VerticalTabs defaultValue="profile">
<VerticalTabsList className="w-64">
<VerticalTabTrigger
value="profile"
icon={<User className="h-4 w-4" />}
description="Personal information"
>
Profile
</VerticalTabTrigger>
<VerticalTabTrigger
value="security"
icon={<Lock className="h-4 w-4" />}
description="Password & 2FA"
>
Security
</VerticalTabTrigger>
<VerticalTabTrigger
value="notifications"
icon={<Bell className="h-4 w-4" />}
description="Email preferences"
>
Notifications
</VerticalTabTrigger>
<VerticalTabTrigger
value="billing"
icon={<CreditCard className="h-4 w-4" />}
description="Plans & payment"
>
Billing
</VerticalTabTrigger>
<VerticalTabTrigger
value="team"
icon={<Users className="h-4 w-4" />}
description="Manage members"
>
Team
</VerticalTabTrigger>
</VerticalTabsList>
<VerticalTabContent value="profile">
<ProfileSettings />
</VerticalTabContent>
<VerticalTabContent value="security">
<SecuritySettings />
</VerticalTabContent>
<VerticalTabContent value="notifications">
<NotificationSettings />
</VerticalTabContent>
<VerticalTabContent value="billing">
<BillingSettings />
</VerticalTabContent>
<VerticalTabContent value="team">
<TeamSettings />
</VerticalTabContent>
</VerticalTabs>
</div>
)
}Dashboard Views
tsx
import { BarChart, Users, DollarSign, TrendingUp } from 'lucide-react'
function DashboardPage() {
return (
<VerticalTabs defaultValue="overview">
<VerticalTabsList>
<VerticalTabTrigger
value="overview"
icon={<BarChart className="h-4 w-4" />}
>
Overview
</VerticalTabTrigger>
<VerticalTabTrigger
value="users"
icon={<Users className="h-4 w-4" />}
>
Users
</VerticalTabTrigger>
<VerticalTabTrigger
value="revenue"
icon={<DollarSign className="h-4 w-4" />}
>
Revenue
</VerticalTabTrigger>
<VerticalTabTrigger
value="analytics"
icon={<TrendingUp className="h-4 w-4" />}
>
Analytics
</VerticalTabTrigger>
</VerticalTabsList>
<VerticalTabContent value="overview">
<DashboardOverview />
</VerticalTabContent>
{/* Other content panels */}
</VerticalTabs>
)
}Best Practices
✅ Do's
- Use descriptive labels - Clear, concise tab names
- Add icons - Improve scannability and recognition
- Provide descriptions - Help users understand tab content
- Limit tab count - 5-8 tabs maximum for usability
- Group related content - Logical organization
- Use consistent widths - Fixed sidebar width for stability
❌ Don'ts
- Don't use on mobile - Not enough horizontal space
- Don't nest tabs - Avoid tabs within tabs
- Don't use for navigation - Use for content switching, not site navigation
- Don't forget accessibility - Ensure keyboard navigation works
- Don't overload tabs - Too many tabs overwhelm users
Accessibility
The component includes full accessibility support:
- ARIA roles:
tablist,tab,tabpanel - ARIA attributes:
aria-selected,aria-controls,aria-labelledby - Keyboard navigation: Arrow keys, Tab, Enter
- Focus management: Clear focus indicators
- Screen reader support: Proper announcements
tsx
// Accessibility is built-in
<VerticalTabs defaultValue="profile">
<VerticalTabsList>
{/* role="tablist" aria-orientation="vertical" */}
<VerticalTabTrigger value="profile">
{/* role="tab" aria-selected aria-controls */}
Profile
</VerticalTabTrigger>
</VerticalTabsList>
<VerticalTabContent value="profile">
{/* role="tabpanel" aria-labelledby */}
Content
</VerticalTabContent>
</VerticalTabs>Animation
Content panels animate in smoothly:
- Fade in: Opacity transition
- Slide in: Subtle right-to-left slide
- Duration: 200ms for snappy feel
The animation is handled automatically using Tailwind's animate-in utilities.
API Reference
For detailed prop documentation, see:
Related Components
- Tabs (shadcn/ui) - Horizontal tabs
- NavigationMenu - For site navigation