Tabs
Switch between associated content panels.
12 open tasks, 3 due this week.
Installation
pnpm dlx shadcn@4.18.0 add @siteplane/tabsComplete the one-time setup first. Registry JSON lists source files and dependencies.
For this page’s examples, also install:
pnpm dlx shadcn@4.18.0 add @siteplane/cardUsage
import {
Tabs,
TabsList,
TabsTab,
TabsPanel,
} from "@/siteplane/components/tabs";
<Tabs defaultValue="overview">
<TabsList>
<TabsTab value="overview">Overview</TabsTab>
</TabsList>
<TabsPanel value="overview">Content</TabsPanel>
</Tabs>;Composition
Tabs
├── TabsRail (optional)
│ └── TabsList
│ └── TabsTab / TabsTrigger
└── TabsPanel / TabsContentTabsRail puts an adjacent action in the same bar as the list, with one shared indicator. Filter Bar builds on it for a Search segment that opens into a field.
List variants
Set variant on TabsList: default, pill or underline. The underline variant can also draw a line under all tabs.
Key figures for this project.
Key figures for this project.
Key figures for this project.
Underline with a divider
Set divider on an underline TabsList to draw a hairline under all tabs; the active underline sits on it. The line spans the list, so w-full runs it across the container while the tabs stay at the start. Vertical tabs draw it along their side.
Your name, photo and contact email.
With icons
Place a decorative icon before the label; the text stays the tab’s accessible name.
Recent activity across your workspace.
Vertical orientation
Set orientation on Tabs so layout and keyboard direction agree. Vertical tabs fill the width of the list, so their labels start at one edge.
Update your name, photo and contact email.
Sizes
Root size flows through List to Tab; a closer explicit size overrides it.
Key figures for this project.
Key figures for this project.
Key figures for this project.
Key figures for this project.
Controlled selection
Match each tab value to its panel. The application owns controlled selection.
Key figures for this project.
More tabs than fit
When the tabs do not fit, the list scrolls, and a scroll button holds its place at each edge. Each button shows while more tabs wait in its direction and moves the list by a page of tabs. The selected tab scrolls into view when the list first overflows and whenever the selection changes, and the arrow keys bring the focused tab into view. The buttons are for a mouse or trackpad. Keyboard users move through the tabs with the arrow keys, so the buttons stay out of the tab order and the accessibility tree. On a touch screen the list scrolls by swiping, so the buttons stay hidden and take no room. Vertical lists and lists inside a TabsRail keep their own scrolling. There is nothing to set up.
API keys and webhooks of the Northlight workspace.
Switching panels
Switching tabs shows the new panel at once, as tabs do in the WAI-ARIA pattern and most interfaces. The indicator glides to the new tab, and the element that holds the panels follows the new panel's height over --motion-duration-expand (200 ms), so the content below never jumps. No moment shows two panels or none. There is nothing to set up. A panel with keepMounted keeps its state through the switch, and reduced motion changes the height at once.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
Tabs. | sm | md | lg | xl | provider / lg | Shared size. |
Tabs. | unknown | Base UI default | Controlled or initial tab. |
Tabs. | (value, eventDetails) => void | unset | Selection change. |
Tabs | TabsVariant: default | pill | underline | default | List and tab treatment. |
Tabs | boolean | false | Underline only: a hairline under all tabs that the active underline sits on. |
Tabs | boolean | false | Suppress own indicator when inside a rail. |
Tabs | sm | md | lg | xl | nearest context / provider | Override size. |
Tabs | default | pill | default | Shared rail treatment. |
Tabs | RefObject<HTMLElement | null> | selected tab in rail | Custom active indicator target. |
Tabs | ReactElement | render function | Siteplane UI Button (text) | Composed tab trigger. |
Tabs | boolean | false | Preserve hidden panel state. |
TabsProps, TabsListProps, TabsTabProps and TabsVariant are exported.
TabsTrigger aliases TabsTab; TabsContent aliases TabsPanel; TabsPrimitive
exports the Base UI namespace. Tab/Panel value, disabled, orientation,
activation, refs and focus follow Base UI
Tabs. Styled classes are strings.
TabsRail accepts native div props and ref and measures its indicator on
selection, resize and scroll; it adds no selection behavior. The indicator is
clipped to the strip that shows the tabs: the list, or the scroll viewport of a
ScrollArea that holds the list inside the rail. Before the page hydrates, the
active tab draws the selection itself, and the indicator takes over in place
once it has measured the tab, so the selection shows from the first paint
without an inline script. TabsList renders its list in a frame with
data-slot="tabs-list-frame", which adds no box while the tabs fit or on a
touch screen and holds the two tabs-scroll-button parts beside the list when
they overflow; the list itself keeps className and the other props. The scroll
takes --motion-duration-moderate and --motion-ease-standard, and reduced
motion makes it instant.
Usage notes
Tab enters the selected tab; arrow keys move focus among enabled tabs, and Enter or Space selects the focused tab. Set activateOnFocus on TabsList to select tabs as they receive focus. Hidden panels unmount unless keepMounted is set, so keep costly state outside the panel or preserve it with keepMounted. The panel that leaves stays mounted below the new one until it has faded out. While the panels swap, the element that holds them is positioned and clipped; a container that scrolls keeps its own height.