Overview
Tabs provides an accessible tab interface with full keyboard navigation support, roving tabindex, and both automatic and manual activation modes. It supports horizontal and vertical orientations.
Sub-Components
- Tabs.Root: Renderless container that manages tab state
- Tabs.List: Container for tab triggers with tablist role
- Tabs.Item: Individual tab trigger with keyboard navigation
- Tabs.Panel: Content panel associated with a tab
Usage
<script lang="ts" setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<Tabs.Root v-model="selected">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
<Tabs.Item value="billing" disabled>Billing</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">Profile content</Tabs.Panel>
<Tabs.Panel value="password">Password content</Tabs.Panel>
<Tabs.Panel value="billing">Billing content</Tabs.Panel>
</Tabs.Root>
</template>
Tabs.Root
Props
namespace
string
default:"'v0:tabs'"
Namespace for dependency injection (must match child namespace)
Disables the entire tabs instance
Auto-select non-disabled items on registration
mandatory
boolean | 'force'
default:"'force'"
Controls mandatory tab behavior:
false: No mandatory tab enforcement
true: Prevents deselecting the last selected item
'force' (default): Automatically selects the first non-disabled tab
Whether arrow key navigation wraps around
orientation
'horizontal' | 'vertical'
default:"'horizontal'"
Tab orientation for keyboard navigation
activation
'automatic' | 'manual'
default:"'automatic'"
Activation mode:
automatic: Tab activates on focus (arrow keys)
manual: Tab activates on Enter/Space only
Model
Slot Props
Whether the tabs instance is disabled
orientation
'horizontal' | 'vertical'
Current orientation
Step forward or backward by a specific count
Toggle a tab’s selection state by ID
Attributes including aria-multiselectable: false
Tabs.List
Props
as
DOMElement | null
default:"'div'"
The HTML element to render as
Accessible label for the tab list
Slot Props
orientation
'horizontal' | 'vertical'
Current orientation
Attributes including role=“tablist” and aria-orientation
Tabs.Item
Props
as
DOMElement | null
default:"'button'"
The HTML element to render as
Unique identifier (auto-generated if not provided)
Value associated with this tab (used to match with TabsPanel)
Disables this specific tab
namespace
string
default:"'v0:tabs'"
Namespace for dependency injection
Accessible label for this tab
ID of element that labels this tab
ID of element that describes this tab
Slot Props
Whether this tab is currently selected
Whether this tab is disabled
Attributes including role, tabindex, aria-selected, aria-controls, event handlers, etc.
Tabs.Panel
Props
as
DOMElement | null
default:"'div'"
The HTML element to render as
Value that matches this panel with a Tabs.Item
namespace
string
default:"'v0:tabs'"
Namespace for dependency injection
Slot Props
Whether this panel is currently visible
Attributes including role=“tabpanel”, aria-labelledby, and tabindex
Examples
Basic
Vertical
Manual Activation
With Disabled Tab
With Slot Props
Non-Circular
<script setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<Tabs.Root v-model="selected">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">
Profile content
</Tabs.Panel>
<Tabs.Panel value="password">
Password content
</Tabs.Panel>
</Tabs.Root>
</template>
<script setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<Tabs.Root v-model="selected" orientation="vertical">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">
Profile content
</Tabs.Panel>
<Tabs.Panel value="password">
Password content
</Tabs.Panel>
</Tabs.Root>
</template>
<script setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<!-- Requires Enter/Space to activate -->
<Tabs.Root v-model="selected" activation="manual">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">
Profile content
</Tabs.Panel>
<Tabs.Panel value="password">
Password content
</Tabs.Panel>
</Tabs.Root>
</template>
<script setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<Tabs.Root v-model="selected">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
<Tabs.Item value="billing" disabled>Billing</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">
Profile content
</Tabs.Panel>
<Tabs.Panel value="password">
Password content
</Tabs.Panel>
<Tabs.Panel value="billing">
Billing content
</Tabs.Panel>
</Tabs.Root>
</template>
<script setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<Tabs.Root v-model="selected">
<Tabs.List label="Account settings">
<Tabs.Item value="profile" v-slot="{ isSelected }">
<span :class="{ 'border-b-2 border-blue-500': isSelected }">
Profile
</span>
</Tabs.Item>
<Tabs.Item value="password" v-slot="{ isSelected }">
<span :class="{ 'border-b-2 border-blue-500': isSelected }">
Password
</span>
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile" v-slot="{ isSelected }">
<div v-if="isSelected">
Profile content
</div>
</Tabs.Panel>
<Tabs.Panel value="password" v-slot="{ isSelected }">
<div v-if="isSelected">
Password content
</div>
</Tabs.Panel>
</Tabs.Root>
</template>
<script setup>
import { ref } from 'vue'
import { Tabs } from '@vuetify/v0'
const selected = ref('profile')
</script>
<template>
<!-- Navigation doesn't wrap around -->
<Tabs.Root v-model="selected" :circular="false">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">
Profile content
</Tabs.Panel>
<Tabs.Panel value="password">
Password content
</Tabs.Panel>
</Tabs.Root>
</template>
Keyboard Navigation
Horizontal (default)
- ArrowRight: Move to next tab
- ArrowLeft: Move to previous tab
- Home: Move to first tab
- End: Move to last tab
- Enter/Space: Activate tab (manual mode only)
Vertical
- ArrowDown: Move to next tab
- ArrowUp: Move to previous tab
- Home: Move to first tab
- End: Move to last tab
- Enter/Space: Activate tab (manual mode only)
Accessibility
- Tabs.List has
role="tablist" and aria-orientation
- Tabs.Item has
role="tab", roving tabindex, and aria-selected
- Tabs.Panel has
role="tabpanel" and aria-labelledby pointing to its tab
- Focus management with keyboard navigation
- Disabled tabs are skipped in navigation
<template>
<Tabs.Root v-model="selected">
<Tabs.List label="Settings">
<Tabs.Item value="profile" aria-label="Profile settings">
Profile
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">
Profile content
</Tabs.Panel>
</Tabs.Root>
</template>
SSR
Tabs is fully compatible with SSR. The Root component is renderless by default:
<template>
<Tabs.Root v-model="selected">
<Tabs.List label="Account settings">
<Tabs.Item value="profile">Profile</Tabs.Item>
<Tabs.Item value="password">Password</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="profile">Profile content</Tabs.Panel>
<Tabs.Panel value="password">Password content</Tabs.Panel>
</Tabs.Root>
</template>