Tabs
Switch between related panels using underline tabs.
Preview
Use tabs for related views on the same page. Underline tabs are the default for page sections.
Installation
pnpm dlx shadcn@latest add http://127.0.0.1:3017/r/tabs.jsonInstalls Tabs with Base UI. Uses your existing cn utility and semantic theme tokens. No theme changes are bundled. See the registry guide for local endpoint details.
Usage
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';Use defaultValue for an uncontrolled initial selection, or value and onValueChange for controlled selection. Values must be unique within a root and match the corresponding panel. Tabs uses Base UI, including render for custom element composition.
Composition
Tabs
├── TabsList
│ ├── TabsTrigger
│ └── TabsTrigger
├── TabsContent
└── TabsContentTabs owns selection and orientation. TabsList groups the triggers and uses an internal Scroll Fade viewport with room for focus outlines. TabsTrigger selects a matching TabsContent. Give each list an accessible name with aria-label or aria-labelledby. Keep every trigger inside its list and each panel inside the same Tabs root.
Icons and disabled tabs
<TabsTrigger value="overview">
<FileText aria-hidden="true" />
Overview
</TabsTrigger>
<TabsTrigger value="settings" disabled>Settings</TabsTrigger>TabsTrigger owns the 16px icon size, 1.75 stroke width, and 8px gap. Keep labels visible. Disabled tabs cannot be activated. Disable a tab when its availability is meaningful; otherwise omit unrelated views.
Overflow
The row scrolls horizontally rather than wrapping. A reusable Scroll Fade viewport fades only the edges with hidden content. The fade clears while a tab has focus to preserve its focus outline. Constrain the Tabs root with layout classes when it sits inside a narrow region. Keyboard focus scrolls the focused tab into view. Prefer concise labels and avoid hiding essential views behind excessive scrolling.
Appearance
| Setting | Default |
|---|---|
| Underline row height | 36px |
| Tab horizontal padding | 12px |
| Gap between tabs | 8px |
| Label | 14px / 20px, medium |
| Active indicator | 2px primary underline, positioned below the row divider |
| Content gap | 16px |
Active labels use foreground; inactive labels use muted foreground. Hover changes text color. Keyboard focus uses the shared solid 2px offset outline independently of selection. Reduced-motion preferences disable color transitions. Vertical orientation is available through orientation="vertical" on Tabs, with a side indicator for the line variant.
Keyboard and accessibility
- Tab enters the tab list at its active tab; arrow keys move focus through tabs.
- Enter or Space activates the focused tab. This manual activation is the default.
- Set
activateOnFocuson TabsList when arrow-key focus should also select immediately. Use this for panels that appear without a noticeable delay. - Home and End move focus to the first and last available tabs. Vertical lists use Up and Down arrows.
- Tab leaves the list and proceeds into panel content. Base UI connects tabs and panels with accessible IDs and roles.
- Inactive panels unmount by default; use
keepMountedon TabsContent when its local state should survive switching. - Tabs switch same-page content. Use links or navigation components for unrelated routes.
API
| Component / prop | Purpose |
|---|---|
Tabs defaultValue | Initial uncontrolled tab |
Tabs value, onValueChange | Controlled selection |
Tabs orientation | horizontal or vertical |
TabsList activateOnFocus | Automatic activation, default false |
TabsList loopFocus | Wrap arrow-key focus, default true |
TabsTrigger value, disabled | Panel identity and availability |
TabsContent value, keepMounted | Matching panel and optional retained mounting |
Native props, refs, and Base UI part props are forwarded. className on TabsList applies to the actual tab list, not its scrolling wrapper.
Based on shadcn Tabs and Base UI Tabs.