NimUI
ComponentsLayout

Tabs

Tabbed interface for organizing content into switchable panels

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.

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.

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.

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)

NameTypeDefaultDescription
defaultValuestring-The value of the tab to select by default (uncontrolled)
valuestring-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)
classNamestring-Additional CSS classes to apply to the root
children*ReactNode-TabsList and TabsContent components

TabsList

NameTypeDefaultDescription
classNamestring-Additional CSS classes to apply to the tab list container
children*ReactNode-TabsTrigger components

TabsTrigger

NameTypeDefaultDescription
value*string-Unique value that links this trigger to its corresponding TabsContent
disabledbooleanfalseWhether the tab trigger is disabled
classNamestring-Additional CSS classes to apply
children*ReactNode-Tab label content

TabsContent

NameTypeDefaultDescription
value*string-Value that links this content panel to its corresponding TabsTrigger
forceMountboolean-Force the content to mount even when inactive (useful for animations)
classNamestring-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

On this page