Tabs
Accessible, unstyled tabset primitive with roving focus and SSR-stable ids.
|
|
| Package |
@vizejs/ui/tabs |
| Maturity |
stable |
| Own the source |
vize lib pull tabs |
| Requires |
collection, composite-navigation, context, controllable-state, typeahead |
| Aliases |
tablist, tab panels, segmented navigation, application sections |
| Covers |
WAI-ARIA tabs pattern, React Aria Tabs, Radix Tabs, Reka UI Tabs |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Tabs, TabsRoot, TabsContent, TabsList, TabsTrigger } from "@vizejs/ui/tabs";
Or copy the source into your project with vize lib pull tabs (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components

Copy the example
<!-- Uncontrolled tabs with a labelled tab list and matching panels. -->
<script setup lang="ts">
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "@vizejs/ui/tabs";
</script>
<template>
<TabsRoot default-value="overview">
<TabsList aria-label="Product sections">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="specs">Specifications</TabsTrigger>
<TabsTrigger value="reviews" disabled>Reviews</TabsTrigger>
</TabsList>
<TabsContent value="overview">A lightweight jacket for everyday wear.</TabsContent>
<TabsContent value="specs">Water resistant, 320 g, machine washable.</TabsContent>
<TabsContent value="reviews">Reviews are coming soon.</TabsContent>
</TabsRoot>
</template>
API
Tabs
Source: src/families/navigation/tabs/tabs-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned Tabs base id. null and undefined select a deterministic fallback. |
modelValue |
TabsValue |
undefined |
Controlled selected tab value. undefined selects uncontrolled behavior; null clears it. |
defaultValue |
TabsValue |
undefined |
Initial selected value for uncontrolled use. undefined selects the first enabled trigger. |
disabled |
boolean |
false |
Disable every trigger while preserving the current selected panel. |
activationMode |
TabsActivationMode |
"automatic" |
Whether arrow focus activates tabs immediately or waits for Enter, Space, or click. |
orientation |
TabsOrientation |
"horizontal" |
Directional layout hint used by roving arrow-key focus. |
dir |
TabsDirection |
undefined |
Reading direction used for horizontal arrow-key navigation. undefined inherits DirectionProvider/LocaleProvider, then "ltr". |
loop |
boolean |
true |
Whether arrow-key navigation wraps at the first and last enabled trigger. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: TabsValue] |
Fired when the selected value requests a new controlled value. |
change |
[value: TabsValue, previous: TabsValue, nativeEvent: Event | null] |
Fired after any distinct selected-value request. |
Slots
| Slot |
Slot props |
Description |
default |
TabsSlotState |
Compound Tabs children. Receives current selection, orientation, and availability state. |
Exposed
| Member |
Type |
Description |
activationMode |
— |
|
dir |
— |
|
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
id |
— |
|
listId |
— |
|
orientation |
— |
|
reset |
— |
|
setValue |
— |
|
state |
— |
|
value |
— |
|
TabsRoot
Source: src/families/navigation/tabs/tabs-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned Tabs base id. null and undefined select a deterministic fallback. |
modelValue |
TabsValue |
undefined |
Controlled selected tab value. undefined selects uncontrolled behavior; null clears it. |
defaultValue |
TabsValue |
undefined |
Initial selected value for uncontrolled use. undefined selects the first enabled trigger. |
disabled |
boolean |
false |
Disable every trigger while preserving the current selected panel. |
activationMode |
TabsActivationMode |
"automatic" |
Whether arrow focus activates tabs immediately or waits for Enter, Space, or click. |
orientation |
TabsOrientation |
"horizontal" |
Directional layout hint used by roving arrow-key focus. |
dir |
TabsDirection |
undefined |
Reading direction used for horizontal arrow-key navigation. undefined inherits DirectionProvider/LocaleProvider, then "ltr". |
loop |
boolean |
true |
Whether arrow-key navigation wraps at the first and last enabled trigger. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: TabsValue] |
Fired when the selected value requests a new controlled value. |
change |
[value: TabsValue, previous: TabsValue, nativeEvent: Event | null] |
Fired after any distinct selected-value request. |
Slots
| Slot |
Slot props |
Description |
default |
TabsSlotState |
Compound Tabs children. Receives current selection, orientation, and availability state. |
Exposed
| Member |
Type |
Description |
value |
TabsValue |
Current selected value, or null when no tab is selected. |
disabled |
boolean |
Whether the root suppresses all trigger activation and roving focus. |
activationMode |
TabsActivationMode |
Whether focus movement activates tabs automatically. |
orientation |
TabsOrientation |
Directional layout hint used by tablist keyboard navigation. |
dir |
TabsDirection |
Reading direction used for horizontal arrow navigation. |
state |
TabsState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered root element. |
id |
string |
Root-owned base id for the Tabs family. |
listId |
string |
Id wired to TabsList. |
focus |
(options?: FocusOptions) => void |
Move focus to the selected, active, or first enabled trigger. |
setValue |
(value: TabsValue, event?: Event | null) => boolean |
Request a selected value update and report whether it differs. |
reset |
() => boolean |
Restore the default value, or the first enabled trigger when no default exists. |
TabsContent
Source: src/families/navigation/tabs/tabs-content.vue
Props
| Prop |
Type |
Default |
Description |
value (required) |
string |
required |
Content value paired with a TabsTrigger. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string | null |
undefined |
Space-separated ids that label the panel. null omits the default trigger id. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the panel. |
Slots
| Slot |
Slot props |
Description |
default |
TabsContentSlotState |
Panel contents. Receives current visibility, orientation, and availability state. |
Exposed
| Member |
Type |
Description |
value |
string |
Content value paired with a TabsTrigger. |
selected |
boolean |
Whether this panel is currently selected and visible. |
disabled |
boolean |
Whether the Tabs root suppresses trigger activation. |
orientation |
TabsOrientation |
Directional layout hint inherited from the Tabs root. |
state |
TabsContentState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered tabpanel element. |
id |
string |
Deterministic id wired from content to trigger. |
focusContent |
(options?: FocusOptions) => void |
Move focus to the visible content panel. |
TabsList
Source: src/families/navigation/tabs/tabs-list.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the tablist. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the tablist. |
Slots
| Slot |
Slot props |
Description |
default |
TabsListSlotState |
TabsTrigger children. Receives the current root and list state. |
Exposed
| Member |
Type |
Description |
value |
TabsValue |
Current selected value, or null when no tab is selected. |
disabled |
boolean |
Whether the root suppresses all trigger activation and roving focus. |
activationMode |
TabsActivationMode |
Whether focus movement activates tabs automatically. |
orientation |
TabsOrientation |
Directional layout hint used by tablist keyboard navigation. |
dir |
TabsDirection |
Reading direction used for horizontal arrow navigation. |
state |
TabsState |
Stable state token for styling and tests. |
listId |
string |
Deterministic id assigned to the tablist. |
element |
HTMLDivElement | null |
Rendered tablist element. |
focus |
(options?: FocusOptions) => void |
Move focus to the selected, active, or first enabled trigger. |
TabsTrigger
Source: src/families/navigation/tabs/tabs-trigger.vue
Props
| Prop |
Type |
Default |
Description |
value (required) |
string |
required |
Trigger value used by the Tabs selection model. |
type |
"button" | "reset" | "submit" |
"button" |
Native button submission behavior. |
disabled |
boolean |
false |
Disable this trigger while preserving the current selected panel. |
textValue |
string | null |
undefined |
Explicit text used by future collection search or virtualized trigger labels. |
order |
number |
undefined |
Deterministic order for virtualized, portalled, or server-only triggers. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the trigger. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the trigger. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before this trigger requests selection. Call preventDefault() to keep state unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
TabsTriggerSlotState |
Trigger contents. Receives current selection, orientation, and availability state. |
indicator |
TabsTriggerSlotState |
Optional indicator slot for consumer-owned active-marker rendering. |
Exposed
| Member |
Type |
Description |
value |
string |
Trigger value used by the Tabs selection model. |
selected |
boolean |
Whether this trigger controls the selected tabpanel. |
disabled |
boolean |
Whether this trigger or its root suppresses activation. |
activationMode |
TabsActivationMode |
Whether focus movement activates tabs automatically. |
orientation |
TabsOrientation |
Directional layout hint inherited from the Tabs root. |
state |
TabsTriggerState |
Stable state token for styling and tests. |
element |
HTMLButtonElement | null |
Rendered native tab button. |
id |
string |
Deterministic id wired from trigger to content. |
focus |
(options?: FocusOptions) => void |
Move focus to the trigger. |
Behavior
Normative state x input -> outcome table for tabs-root.vue, tabs-list.vue,
tabs-trigger.vue, and tabs-content.vue (@vizejs/ui/tabs). Every row in
this isolated slice is proven by the named focused tests; registry, package
exports, renderer fixtures, and size budgets are intentionally left for the
parent integration step.
| ID |
State |
Input |
Outcome |
Evidence |
| T1 |
default / selected |
render |
root, tablist, tab, and tabpanel expose deterministic ids, ARIA wiring, slots, and data |
renders accessible tab semantics with deterministic ids and slots |
| T2 |
uncontrolled automatic |
Arrow key |
roving focus skips disabled triggers and immediately selects the focused tab |
automatic activation follows roving focus and skips disabled triggers |
| T3 |
uncontrolled manual |
Arrow key, then Space |
focus moves without selection until native activation requests the focused tab |
manual activation waits for keyboard or pointer activation |
| T4 |
controlled |
click |
emits requested value while rendered selection stays controlled until parent accepts it |
controlled value wins until the parent accepts the request |
| T5 |
disabled root / trigger |
click or Tab |
disabled triggers leave activation and sequential focus while preserving content state |
disabled roots and triggers suppress activation and focus |
| T6 |
exposed instances |
focus, setValue, reset |
public refs expose element state and imperative focus/value methods |
exposes typed state and imperative focus/value controls |
| T7 |
missing provider |
setup |
compound parts fail closed with the shared context diagnostic |
compound parts require a matching root provider |
| T8 |
SSR and hydration |
isolated render/mount |
generated ids are byte-identical per request and hydrate without replacement warnings |
tabs-ssr.test.ts |
Content panels use hidden while inactive, retain stable tabpanel markup for
hydration, and default aria-labelledby to the paired trigger id. Trigger
indicator is a named slot for consumer-owned active marker rendering; no
visual styling ships with the primitive.