Search for a command to run...
Switch between peer views without leaving the page. Pill segmented chrome; a single motion indicator slides between triggers on change. Use for peer content (overview / activity / settings) — never for top-level navigation.
The active indicator is a single motion.span shared across triggers via layoutId, so the pill slides between positions on change instead of swapping bg-colors per trigger.
TabsThe state container. Forwards every Radix Tabs.Root prop.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled selected tab. |
defaultValue | string | — | Uncontrolled initial tab. |
onValueChange | (value: string) => void | — | Fires on tab change. |
orientation | "horizontal" | "vertical" | "horizontal" | vertical stacks the list and rotates the active indicator to the right edge. |
activationMode | "automatic" | "manual" | "automatic" | Whether arrow-key focus also activates the tab. |
TabsListThe trigger row. Forwards every Radix Tabs.List prop.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Override the default. Common overrides: w-full to stretch the list; flex-col for vertical orientation; gap-N to tune spacing. |
TabsTriggerA single tab button. Forwards every Radix Tabs.Trigger prop. Wraps children in Slot.Slottable so asChild works alongside the internal motion indicator.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | The tab's value. Required. |
disabled | boolean | false | Disables this trigger. |
asChild | boolean | false | Render the child element instead of a <button> (e.g. a Next.js <Link> for route-driven tabs). Works correctly with the motion indicator thanks to Slot.Slottable. |
indicatorClassName | string | — | Per-trigger override for the motion indicator's bg/shape. Lets callers tint the sliding pill (e.g. bg-blue-9 on a "Train" trigger only) without breaking the shared layoutId morph. Specify both light + dark: modes when overriding. |
TabsContentThe panel paired with a trigger. Forwards every Radix Tabs.Content prop.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Must match the corresponding TabsTrigger's value. |
forceMount | boolean | false | Always render in the DOM — useful for keeping form state on swap. |
Use Tabs for peer views inside a panel — Overview / Activity / Settings, Me / My Delphi, Train / Preview. The pill stays tight and self-contained.
Stretch the list with w-full on TabsList or flex-1 on TabsTrigger. The pill is
intrinsic-width by design; full-width tabs read as a row of buttons and break the segmented
feel.
Compose with asChild + <Link> for route-driven tabs (one URL per tab). Radix keeps
keyboard nav working and the motion indicator still slides between routes.
Override the indicator per trigger when one tab carries a privileged meaning (e.g. a blue
pill on the "Train" mode tab). Pass both light + dark: classes so the override wins in both
modes — tailwind-merge keeps the primitive's dark:-prefixed defaults separate from your
unprefixed classes.
Use Tabs for top-level app navigation. Reach for Sidebar or NavigationMenu — those handle
deep linking, mobile collapse, and active-state inference from the URL.
Use Tabs for binary toggles. Switch (instant), Toggle (single chip), or a two-option
ToggleGroup read more directly.
Stack 6+ triggers in a single list. Past that the row gets noisy — break the content into
multiple sections, or use a Select for the secondary axis.