Tabs

5 parts

A set of layered sections of content — known as tab panels — where only one panel is shown at a time, each surfaced by its own trigger. Use it to split related content into switchable views without leaving the page: settings panes, dashboards, or product detail sections. The root owns the selected value (controlled via v-model or uncontrolled via defaultValue), orientation, keyboard roving focus across triggers, and provides context to every TabsList, TabsTrigger, and TabsContent.

Demo

Loading demo…

Anatomy

Import the parts and compose them. Each part forwards attributes to its underlying element.

vue
import {
  TabsRoot,
  TabsList,
  TabsTrigger,
  TabsContent,
  TabsIndicator,
} from '@robonen/primitives/disclosure/tabs';

<TabsRoot>
  <TabsList />
  <TabsTrigger />
  <TabsContent />
  <TabsIndicator />
</TabsRoot>

API Reference

TabsRoot

Root

A set of layered sections of content — known as tab panels — where only one panel is shown at a time, each surfaced by its own trigger. Use it to split related content into switchable views without leaving the page: settings panes, dashboards, or product detail sections. The root owns the selected value (controlled via `v-model` or uncontrolled via `defaultValue`), orientation, keyboard roving focus across triggers, and provides context to every `TabsList`, `TabsTrigger`, and `TabsContent`.

PropTypeDescription
modelValue?ValueControlled selected value. Bind with v-model.
defaultValue?ValueUncontrolled initial value.
orientation?'horizontal' | 'vertical'Orientation of the tab list.
dir?RovingDirectionWriting direction. When omitted, inherits from a ConfigProvider, falling back to 'ltr'.
loop?booleanWrap keyboard navigation.
disabled?booleanDisable all tabs.
activationMode?'automatic' | 'manual'How tabs are activated.
unmountOnHide?booleanUnmount inactive panels from the DOM instead of keeping them mounted but hidden. When false, panels stay mounted (hidden) so their state/animation survives switching. Individual panels can opt in via forceMount.

TabsList

List

Container that groups the tab triggers and exposes them as an ARIA `tablist`. Place one inside `TabsRoot`, wrapping the set of `TabsTrigger` elements.

No props or events — renders its element and forwards attributes.

TabsTrigger

Trigger

The clickable control that activates its associated `TabsContent` panel. Selecting it (by click or keyboard) sets the root value to its `value`. Render one per tab inside a `TabsList`.

PropTypeDescription
valueTabsValueValue that links this trigger to a content panel.
disabled?booleanDisable this trigger.

TabsContent

Content

The panel shown when its matching `TabsTrigger` is active. Mounted via `Presence` so enter/leave transitions can run before it leaves the DOM; use `forceMount` to keep it mounted for custom animation, or rely on the root's `unmountOnHide` to keep inactive panels mounted-but-hidden. Pair one with each trigger via a shared `value`.

PropTypeDescription
valueTabsValueValue that links this panel to a trigger.
forceMount?booleanKeep content mounted even when inactive.

TabsIndicator

Indicator

An optional visual cue (e.g. an underline or pill) that tracks the currently active trigger. Render it inside `TabsList`; it exposes the active tab's size and position as CSS variables (`--primitives-tabs-indicator-size` / `--primitives-tabs-indicator-position`) so consumers can animate a highlight purely from CSS.

No props or events — renders its element and forwards attributes.