Skip to content

Tabs

The Tabs component organizes content into switchable panels, allowing users to view one section at a time. Built on Radix UI Tabs, it provides full keyboard navigation and ARIA support. It uses a compound component pattern with TabsList, TabsTrigger, and TabsContent sub-components.

Import

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@nim-ui/components';

Basic Usage

Basic Tabs

Content for Tab 1. This is the first panel.

View Code
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
</TabsList>
<TabsContent value="tab1">
<p>Content for Tab 1.</p>
</TabsContent>
<TabsContent value="tab2">
<p>Content for Tab 2.</p>
</TabsContent>
<TabsContent value="tab3">
<p>Content for Tab 3.</p>
</TabsContent>
</Tabs>

With Rich Content

Tabs with Rich Content

Product Overview

A comprehensive solution for modern web development. Built with performance and developer experience in mind.

View Code
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="features">Features</TabsTrigger>
<TabsTrigger value="pricing">Pricing</TabsTrigger>
</TabsList>
<TabsContent value="overview">
<h3>Product Overview</h3>
<p>A comprehensive solution for modern web development.</p>
</TabsContent>
<TabsContent value="features">
<h3>Key Features</h3>
<ul>
<li>TypeScript support</li>
<li>Accessible by default</li>
<li>Dark mode built-in</li>
<li>Responsive design</li>
</ul>
</TabsContent>
<TabsContent value="pricing">
<h3>Pricing Plans</h3>
<p>Free for open source. Enterprise plans available.</p>
</TabsContent>
</Tabs>

Disabled Tab

Individual tabs can be disabled to prevent selection.

Disabled Tab

This tab is active.

View Code
<Tabs defaultValue="active">
<TabsList>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="disabled" disabled>Disabled</TabsTrigger>
<TabsTrigger value="another">Another</TabsTrigger>
</TabsList>
<TabsContent value="active">This tab is active.</TabsContent>
<TabsContent value="another">This tab is also available.</TabsContent>
</Tabs>

Controlled Tabs

Control the active tab externally using React state.

function ControlledTabs() {
const [activeTab, setActiveTab] = useState('tab1');
return (
<div>
<p>Current tab: {activeTab}</p>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content 1</TabsContent>
<TabsContent value="tab2">Content 2</TabsContent>
<TabsContent value="tab3">Content 3</TabsContent>
</Tabs>
<Button onClick={() => setActiveTab('tab2')}>
Go to Tab 2
</Button>
</div>
);
}

Component Architecture

ComponentDescription
TabsRoot component that manages active tab state (Radix Tabs.Root)
TabsListContainer for tab triggers, styled as a pill bar
TabsTriggerClickable tab button that activates a panel (Radix Tabs.Trigger)
TabsContentPanel content shown when its corresponding trigger is active (Radix Tabs.Content)

Props

Tabs (Root)

Name Type Default Description
defaultValue string - The value of the tab to select by default (uncontrolled)
value string - The controlled active tab value
onValueChange (value: string) => void - Callback when the active tab changes
orientation 'horizontal' | 'vertical' 'horizontal' Orientation of the tab list for keyboard navigation
dir 'ltr' | 'rtl' - Reading direction for keyboard navigation
activationMode 'automatic' | 'manual' 'automatic' Whether tabs activate on focus (automatic) or on click (manual)
className string - Additional CSS classes to apply to the root
children * ReactNode - TabsList and TabsContent components

TabsList

Name Type Default Description
className string - Additional CSS classes to apply to the tab list container
children * ReactNode - TabsTrigger components

TabsTrigger

Name Type Default Description
value * string - Unique value that links this trigger to its corresponding TabsContent
disabled boolean false Whether the tab trigger is disabled
className string - Additional CSS classes to apply
children * ReactNode - Tab label content

TabsContent

Name Type Default Description
value * string - Value that links this content panel to its corresponding TabsTrigger
forceMount boolean - Force the content to mount even when inactive (useful for animations)
className string - Additional CSS classes to apply
children * ReactNode - Tab panel content

Usage Examples

Settings Page

function SettingsPage() {
return (
<Container maxWidth="lg">
<h1 className="text-2xl font-bold mb-6">Settings</h1>
<Tabs defaultValue="profile">
<TabsList>
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="notifications">Notifications</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
</TabsList>
<TabsContent value="profile">
<Card>
<CardHeader>
<h2 className="text-lg font-semibold">Profile Settings</h2>
</CardHeader>
<CardContent>
<Stack spacing="md">
<Input label="Display Name" />
<Textarea label="Bio" rows={3} />
<Button variant="primary">Save Profile</Button>
</Stack>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="account">
<Card>
<CardContent>Account settings content</CardContent>
</Card>
</TabsContent>
<TabsContent value="notifications">
<Card>
<CardContent>Notification preferences</CardContent>
</Card>
</TabsContent>
<TabsContent value="billing">
<Card>
<CardContent>Billing information</CardContent>
</Card>
</TabsContent>
</Tabs>
</Container>
);
}

Product Details

function ProductDetails({ product }) {
return (
<Tabs defaultValue="description">
<TabsList>
<TabsTrigger value="description">Description</TabsTrigger>
<TabsTrigger value="specs">Specifications</TabsTrigger>
<TabsTrigger value="reviews">
Reviews ({product.reviewCount})
</TabsTrigger>
</TabsList>
<TabsContent value="description">
<div className="prose">
<p>{product.description}</p>
</div>
</TabsContent>
<TabsContent value="specs">
<Table
data={product.specs}
columns={[
{ key: 'name', label: 'Specification' },
{ key: 'value', label: 'Value' },
]}
/>
</TabsContent>
<TabsContent value="reviews">
<Stack spacing="md">
{product.reviews.map((review) => (
<Card key={review.id}>
<CardContent>
<p className="font-semibold">{review.author}</p>
<p>{review.text}</p>
</CardContent>
</Card>
))}
</Stack>
</TabsContent>
</Tabs>
);
}

Dashboard with Tabs

function DashboardTabs() {
return (
<Tabs defaultValue="analytics">
<Flex justify="between" align="center">
<TabsList>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
<TabsTrigger value="reports">Reports</TabsTrigger>
<TabsTrigger value="logs">Logs</TabsTrigger>
</TabsList>
<Button variant="outline" size="sm">Export</Button>
</Flex>
<TabsContent value="analytics">
<Grid cols={3} gap="md">
<Card><CardContent>Chart 1</CardContent></Card>
<Card><CardContent>Chart 2</CardContent></Card>
<Card><CardContent>Chart 3</CardContent></Card>
</Grid>
</TabsContent>
<TabsContent value="reports">
<p>Reports content here</p>
</TabsContent>
<TabsContent value="logs">
<p>System logs here</p>
</TabsContent>
</Tabs>
);
}

Accessibility

The Tabs component is built on Radix UI Tabs and follows the WAI-ARIA Tabs Pattern:

  • ARIA roles: Automatically applies role="tablist", role="tab", and role="tabpanel"
  • ARIA attributes: Sets aria-selected, aria-controls, and aria-labelledby on the appropriate elements
  • Focus management: Focus moves between tabs using arrow keys within the tab list
  • Activation modes: Supports both automatic (activate on focus) and manual (activate on Enter/Space) modes

Keyboard Support

KeyAction
Arrow RightMove focus to next tab (horizontal orientation)
Arrow LeftMove focus to previous tab (horizontal orientation)
Arrow DownMove focus to next tab (vertical orientation)
Arrow UpMove focus to previous tab (vertical orientation)
HomeMove focus to first tab
EndMove focus to last tab
Enter / SpaceActivate the focused tab (manual activation mode)
TabMove focus into the active tab panel

Best Practices

  • Use descriptive tab labels that clearly indicate the panel content
  • Keep the number of tabs reasonable (typically 2-7 tabs)
  • Ensure tab content is logically grouped
  • Consider activationMode="manual" when tab switching triggers expensive operations
  • Card - Content container commonly used within tab panels
  • Modal - Dialog overlay for focused interactions
  • Container - Responsive layout wrapper