Tabs
Accessible tabs component with multiple variants, orientations, and keyboard navigation for organizing content
Installation
Install the Tabs component using the nocta-ui CLI:
npx nocta-ui add tabs
Then import the component:
import {
Tabs,
TabsList,
TabsTrigger,
TabsContent
} from '@/components/ui/tabs';
Basic Usage
The Tabs component follows a composable architecture where you build tabbed interfaces by combining smaller components together.
Overview
View your account overview and recent activity.
This is the overview tab content. You can add any components here.
Tabs Architecture
The Tabs component is built with a composable architecture:
- Tabs: The main container that manages state and provides context
- TabsList: Container for tab triggers with keyboard navigation
- TabsTrigger: Individual tab buttons that can be clicked or navigated
- TabsContent: Content panels that are shown/hidden based on selection
Variants
The Tabs component supports three visual variants: default
, pills
, and underline
.
Default
Account settings and profile information.
Pills
Welcome to the home page content.
Underline
Your dashboard overview and quick stats.
Sizes
Three size options are available: sm
, md
, and lg
. The default size is md
.
Small
Small tab content.
Medium
Medium tab content.
Large
Large tab content.
Vertical Orientation
Tabs can be displayed vertically using the orientation="vertical"
prop.
General Settings
Manage your general account settings and preferences.
This is your public display name.
Your email address for notifications.
Disabled States
Individual tabs can be disabled using the disabled
prop on TabsTrigger
.
This tab is available and can be accessed.
Keyboard Navigation
The Tabs component includes comprehensive keyboard support:
- Arrow Keys: Navigate between tabs (Left/Right for horizontal, Up/Down for vertical)
- Home: Jump to the first tab
- End: Jump to the last tab
- Tab: Move focus to the active tab content
- Space/Enter: Activate the focused tab
Accessibility Features
The Tabs component is built with accessibility in mind:
- ARIA Attributes: Proper
role
,aria-selected
,aria-controls
, andaria-labelledby
attributes - Keyboard Navigation: Full keyboard support with arrow keys and shortcuts
- Focus Management: Proper focus handling and visual focus indicators
- Screen Reader Support: Announces tab state changes and content
- Semantic HTML: Uses proper button and panel elements
Controlled vs Uncontrolled
Uncontrolled (Recommended)
Use defaultValue
for uncontrolled behavior:
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content 1</TabsContent>
<TabsContent value="tab2">Content 2</TabsContent>
</Tabs>
Controlled
Use value
and onValueChange
for controlled behavior:
const [activeTab, setActiveTab] = useState("tab1");
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content 1</TabsContent>
<TabsContent value="tab2">Content 2</TabsContent>
</Tabs>
Customization
Custom Styling
All tabs components accept a className
prop for custom styling:
<Tabs defaultValue="tab1" className="w-full max-w-2xl">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="tab1" className="data-[state=active]:bg-blue-500">
Custom Tab 1
</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
</TabsList>
<TabsContent value="tab1" className="border-t-2 border-blue-500 pt-4">
<p>Custom styled content</p>
</TabsContent>
</Tabs>
Dynamic Tabs
Create tabs dynamically from data:
const tabs = [
{ id: 'overview', label: 'Overview', content: 'Overview content' },
{ id: 'details', label: 'Details', content: 'Details content' },
{ id: 'settings', label: 'Settings', content: 'Settings content' }
];
<Tabs defaultValue={tabs[0].id}>
<TabsList>
{tabs.map((tab) => (
<TabsTrigger key={tab.id} value={tab.id}>
{tab.label}
</TabsTrigger>
))}
</TabsList>
{tabs.map((tab) => (
<TabsContent key={tab.id} value={tab.id}>
<Card>
<CardContent>
<p>{tab.content}</p>
</CardContent>
</Card>
</TabsContent>
))}
</Tabs>
Props
Tabs
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | — | Tabs content |
value | string | — | Controlled active tab value |
defaultValue | string | — | Default active tab value |
onValueChange | (value: string) => void | — | Callback when tab changes |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Tabs orientation |
variant | 'default' | 'pills' | 'underline' | 'default' | Visual variant |
size | 'sm' | 'md' | 'lg' | 'md' | Size variant |
className | string | '' | Additional CSS classes |
disabled | boolean | false | Disable all tabs |
TabsList
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | — | Tab triggers |
className | string | '' | Additional CSS classes |
TabsTrigger
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | — | Trigger content |
value | string | — | Unique tab identifier |
className | string | '' | Additional CSS classes |
disabled | boolean | false | Disable this tab |
TabsContent
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | — | Content to display |
value | string | — | Tab identifier this content belongs to |
className | string | '' | Additional CSS classes |
The Tabs components also accept all standard HTML attributes for their respective elements.