Tabs

A set of layered sections of content—known as tab panels—that are displayed one at a time.

Segments and audience rules.
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/tabs.json
Usage
Import from the master file and pair `TabsTrigger` values with `TabsContent` values.
TSImport
import { Tabs } from "@/components/ui/tabs";
TSExample
import { Tabs } from "@/components/ui/tabs";

export function Example() {
  return (
    <Tabs
      defaultValue="account"
      items={[
        { value: "account", label: "Account", content: "Make changes to your account." },
        { value: "password", label: "Password", content: "Change your password." },
      ]}
    />
  );
}
Composition
Anatomy of the Tabs primitive.
Tabs                              (single component — pass items as data)
├── variant="default" | "segment" | "underline" | "underline-card" | "rounded"
├── size="default" | "sm" | "lg"   (sm = h-8, compact)
├── align="start" | "center" | "end"   (underline variants)
├── addItems / onAddItem / addTooltip  (trailing "+" button)
├── subNav="dropdown" | "rail"     (subItems as a caret menu, or a
│                                   vertical rail inside the panel)
├── items={[
│     { value, label, icon?, content?, subItems?, defaultSubValue?, disabled?, triggerClassName?, contentClassName? },
│     …
│   ]}
└── ...rest                       (defaultValue / value / onValueChange / orientation)

Auto-emits TabsList + TabsTrigger per item; TabsContent per item that has
`content`. Items without `content` render a trigger only — useful for
toolbar-style segmented controls (e.g. a theme switcher).

Need a custom panel layout, asynchronous content loading, or a TabsList
sandwiched between hand-written rows? The slot exports — TabsList,
TabsTrigger, TabsContent — remain available as the escape hatch.
Default
Rounded pill container with a primary-colored active label and a soft shadow.
Segments and audience rules.
Small
`size="sm"` shrinks the tab list to `h-8` for compact, inline filter rows.
Theme switcher
The default small tabs with colored icons — drop-in for a light/dark/system toggle.
Underline
Plain text labels with an animated underline that slides between active tabs. Use for page-level section navigation where the segmented look is too heavy.
Segments and audience rules.
Underline (large)
Pass `size="lg"` with the underline variant for prominent, nav-style tabs — a larger label and bigger icons. Great as the primary section nav at the top of a page.
Everything queued for this project.
Underline with dropdowns
Give an item `subItems` and its tab grows a caret. Tapping the **caret** opens that section's sub-navigation; tapping the **tab** selects it and jumps to its first sub item. The active sub item is check-marked in the menu, and `onSubValueChange` reports it. ArrowDown on a focused tab opens the menu, and so does a press-and-hold on the tab itself (touch-friendly — no caret to hit). Tabs without `subItems` behave exactly as before. Clicking the caret again dismisses the menu. For a sub-nav that lives in the panel instead of a menu, see `subNav="rail"` below.
Deliverables → In progress
Underline card
`variant="underline-card"` keeps the underline strip along the top and drops each panel into a `card-surface` container below it. Pair it with `align="start" | "center" | "end"` to place the tabs along the strip, and `panelClassName` to adjust the panel (padding defaults to `p-4`).
Segments and audience rules.
Underline card with dropdowns
The card variant is the underline strip underneath, so `subItems` work unchanged — the caret menu, press-and-hold and `onSubValueChange` all behave exactly as on the plain underline tabs. Shown here with `align="center"`.
Deliverables → In progress
Underline card with add button
Pass `addItems` and a trailing **+** appears at the end of the strip; clicking it opens a menu of tabs the user can add, and `onAddItem` fires with the chosen value. Tabs never mutates `items` itself — the consumer owns that array, so append to it in the handler (and select the new tab, as this demo does). Give `addItems` an empty list and the **+** becomes a plain button that just fires `onAddItem` with no value. `addTooltip` labels the button.
Logos, colors, and brand voice.
Underline card with vertical sub-nav
`subNav="rail"` presents a tab's `subItems` as a vertical underline rail **inside** the card instead of a caret dropdown — horizontal tabs along the top, sub-navigation down the card's left edge. It reads the exact same data as the dropdown mode (`subItems`, `defaultSubValue`, `onSubValueChange`); only the presentation differs, and the trigger grows no caret. Give each sub item its own `content` (it falls back to the parent tab's `content`), and size the rail with `subNavListClassName` (defaults to `w-48`). The rail runs at `size="sm"` so it reads as sub-navigation, sits flush on the card's left border (which becomes its rule, mirroring the top strip), and its first item lines up with the panel's first line. Tabs without `subItems` render their own `content` as usual — the **Activity** tab here does.
Six deliverables are being worked on right now.

Last sub item picked: —

Vertical
Pass `orientation="vertical"` to stack the tabs into a left-hand rail with the underline indicator running down its edge; content sits beside it. Give the list a width (`listClassName="w-48"`) so the rail doesn't go ragged as labels change. Icons work exactly as they do horizontally.
Everything queued for this project.
Rounded
`variant="rounded"` is a Notion-style icon nav — tabs collapse to their icon and the active one expands into a labelled rounded-full pill (the label's grid column animates from `0fr` to `1fr`). No container surface; each tab is its own pill.

Conversations and threads.

API Reference
Props exposed by the Tabs component.

Tabs

PropTypeDefaultDescription
itemsTabsItem[]—List of tab specs. Without this prop, Tabs stays compound (drop in TabsList + TabsTrigger + TabsContent children).
variant"default" | "segment" | "underline" | "underline-card" | "rounded""default"Pill container, compact segmented control, sliding underline nav, the same underline nav with each panel in a card container below it, or Notion-style rounded icon tabs that expand the active label.
align"start" | "center" | "end""start"Where the triggers sit along the strip. Underline variants only — the pill variants are width-fit, so alignment is the parent's job.
panelClassNamestring—`underline-card` only — extra classes for the card panel wrapping each item's content (padding defaults to `p-4`).
addItemsTabsSubItem[]—Adds a trailing **+** button to the strip that opens a menu of these options. Omit it (but keep `onAddItem`) and the + is a plain button with no menu.
onAddItem(value?: string) => void—Fires with the chosen option's value — or with nothing when there's no menu. Append to your own `items` array here; Tabs never adds a tab itself.
addTooltipReact.ReactNode"Add tab"Hover tooltip and accessible label for the + button.
subNav"dropdown" | "rail""dropdown"How a tab's `subItems` are presented — a caret menu on the trigger, or a vertical underline rail inside the panel. Both read the same data and report through `onSubValueChange`.
subNavListClassNamestring—`subNav="rail"` only — classes for the rail's list. Width lives here; defaults to `w-48`.
size"default" | "sm" | "lg""default"Tab scale. `sm` shrinks the pill list to `h-8` for compact, inline filter rows, and gives the underline variants a smaller label, smaller icons and a tighter row. `lg` is the prominent nav-style scale.
fluidbooleanfalseFill the available width with content-proportional tabs — each trigger grows/shrinks from its natural label width (flex-auto), so long labels keep more room than short ones when space is tight. Great for full-width tab rows on mobile. Applies to `default` and `segment` variants.
defaultValuestring—Uncontrolled initial active tab.
valuestring—Controlled active tab value.
onValueChange(value: string) => void—Fires when the active tab changes.
orientation"horizontal" | "vertical""horizontal"Drives flex direction and arrow-key navigation.
listClassNamestring—Class names applied to the auto-emitted `TabsList`.

TabsItem

PropTypeDefaultDescription
valuestring—Identifier matching a `TabsContent` panel.
labelReactNode—Trigger label.
iconReactNode—Leading icon rendered before the label.
contentReactNode—Panel content. Omit to render a trigger-only tab (e.g. for segmented control patterns).
subItemsTabsSubItem[]—Sub-navigation for this tab: `{ value, label, icon?, disabled? }`. Adds a caret that opens the menu; tapping the tab itself jumps to the first sub item, and pressing and holding it opens the menu.
defaultSubValuestring—Uncontrolled initial sub item for this tab. Defaults to the first entry in `subItems`.
onSubValueChange(value: string) => void—Fires when this tab's sub item changes — from the caret menu or from tapping the tab (which selects the first sub item).
disabledboolean—Skip this tab in keyboard navigation.
triggerClassNamestring—Per-tab trigger class names.
contentClassNamestring—Per-tab panel class names.