Vesyl UI

Tabs

Switch between sections of related content within a single view. Built on base-ui Tabs — keyboard navigation, roving focus, and ARIA wiring come for free.

Compose four parts: Tabs (root, owns the active value), TabsList (the trigger strip), TabsTrigger (one per section), and TabsContent (the panel for a value).

Default

The default variant is a filled segmented control — triggers ride on a muted track and the active one lifts onto a solid background.

A summary of the account, its plan, and recent usage.

Line (underline)

variant="line" drops the track and marks the active tab with a 2px underline. This is the right pick for page- or section-level navigation where the tabs sit above their content — e.g. the Users / Permissions split on a role detail page.

Members assigned to this role.

Pill

variant="pill" is a borderless strip — transparent rail, muted background on the active trigger only. Good for filter bars and in-card switches where a full segmented control would feel heavy.

Every order, regardless of status.

With icons

Each TabsTrigger lays out its children in a row with a gap, and auto-sizes any leading svg to size-4. Drop a lucide icon in front of the label.

In-transit and delivered shipments.

Sizes

size controls trigger height and text size: md (default) or sm for dense toolbars. Set it once on TabsList — triggers inherit it.

size="sm" — compact, for dense toolbars.
size="md" — the default.

Vertical orientation

Set orientation="vertical" on the root to stack the list beside its content — the underline / active rail moves to the trigger’s trailing edge. Paired with variant="pill", this is the in-dialog settings-nav pattern (Profile / Billing / Notifications).

Name, email, and avatar.

Controlled

Tabs is uncontrolled by default — give it defaultValue. To drive it yourself (e.g. to sync the active tab to the URL), pass value and onValueChange.

const [tab, setTab] = useState<"users" | "permissions">("users");

<Tabs value={tab} onValueChange={(v) => setTab(v as typeof tab)}>
  <TabsList variant="line">
    <TabsTrigger value="users">Users</TabsTrigger>
    <TabsTrigger value="permissions">Permissions</TabsTrigger>
  </TabsList>
  {/* … */}
</Tabs>;

Reference

Tabs

Root container. Extends base-ui Tabs.Root props.

PropTypeDefaultDescription
defaultValueanyInitially active tab value (uncontrolled)
valueanyActive tab value (controlled)
onValueChange(value) => voidFires when the active tab changes
orientation"horizontal" | "vertical""horizontal"Layout direction; also moves the active indicator
classNamestringMerged onto the root

TabsList

The trigger strip. Extends base-ui Tabs.List props.

PropTypeDefaultDescription
variant"default" | "line" | "pill""default"Filled segmented control, underline, or borderless strip
size"md" | "sm""md"Trigger height and text size; inherited by triggers

TabsTrigger

One tab button. Extends base-ui Tabs.Tab props.

PropTypeDefaultDescription
valueanyIdentifies which panel this opens

TabsContent

A panel. Extends base-ui Tabs.Panel props.

PropTypeDefaultDescription
valueanyMatches the trigger that reveals it