Tabs
A tab navigation component for switching between content sections. Supports line, card, and pill variants, icons, closable tabs, content panels, and four positions (top, bottom, left, right).
Basic
<script setup>
import { ref } from 'vue'
import { VxTabs } from 'veloxui'
const active = ref('home')
const items = [
{ key: 'home', label: 'Home' },
{ key: 'profile', label: 'Profile' },
{ key: 'settings', label: 'Settings' },
]
</script>
<template>
<VxTabs v-model="active" :items="items" />
</template>With Icons
<VxTabs v-model="active" :items="[
{ key: 'overview', label: 'Overview', icon: 'home' },
{ key: 'analytics', label: 'Analytics', icon: 'chart' },
{ key: 'settings', label: 'Settings', icon: 'settings' },
{ key: 'users', label: 'Users', icon: 'user' },
]" />Disabled Tab
<VxTabs v-model="active" :items="[
{ key: 'general', label: 'General' },
{ key: 'security', label: 'Security' },
{ key: 'billing', label: 'Billing', disabled: true },
{ key: 'notifications', label: 'Notifications' },
]" />With Badges ^0.1.9
Set badge on a tab item to show a counter. Values greater than 9 display as +9 — hover to see the full number in a tooltip. Use dot: true for a pulse indicator.
<script setup>
import { ref } from 'vue'
import { VxTabs } from 'veloxui'
const active = ref('inbox')
const items = [
{ key: 'inbox', label: 'Inbox', badge: 3 },
{ key: 'drafts', label: 'Drafts', badge: 42 }, // shows "+9", title="42"
{ key: 'spam', label: 'Spam', badge: 128 }, // shows "+9", title="128"
{ key: 'live', label: 'Live', dot: true },
]
</script>
<template>
<VxTabs v-model="active" :items="items" />
</template>Full Badge (no truncation)
Set fullBadge: true on an item to always show the complete value, even above 9.
<VxTabs v-model="active" :items="[
{ key: 'inbox', label: 'Inbox', badge: 3, fullBadge: true },
{ key: 'drafts', label: 'Drafts', badge: 42, fullBadge: true },
{ key: 'spam', label: 'Spam', badge: 128, fullBadge: true },
]" />Badges on Segment Variant
Badges render the same way inside every variant — here's the segment style.
<script setup>
import { ref } from 'vue'
import { VxTabs } from 'veloxui'
const active = ref('all')
const items = [
{ key: 'all', label: 'All', badge: 132 }, // shows "+9", title="132"
{ key: 'open', label: 'Open', badge: 8 },
{ key: 'closed', label: 'Closed', badge: 24 },
]
</script>
<template>
<VxTabs v-model="active" :items="items" variant="segment">
<template #panel-all>All issues content…</template>
<template #panel-open>Open issues content…</template>
<template #panel-closed>Closed issues content…</template>
</VxTabs>
</template>Card Variant
<VxTabs v-model="active" :items="items" variant="card" />Pill Variant
<VxTabs v-model="active" :items="items" variant="pill" />Segment Variant
Segmented pill-style tabs with a bordered card body below.
<script setup>
import { ref } from 'vue'
import { VxTabs, VxInput, VxButton, VxSwitch } from 'veloxui'
const active = ref('home')
const items = [
{ key: 'home', label: 'Home' },
{ key: 'profile', label: 'Profile' },
{ key: 'settings', label: 'Settings' },
]
</script>
<template>
<VxTabs v-model="active" :items="items" variant="segment">
<template #panel-home>
<p>Welcome back! Here is an overview of your activity.</p>
</template>
<template #panel-profile>
<VxInput placeholder="Display name" />
<VxButton size="sm">Save</VxButton>
</template>
<template #panel-settings>
<div>Dark mode <VxSwitch /></div>
<div>Notifications <VxSwitch /></div>
</template>
</VxTabs>
</template>Folder Variant
Classic folder-tab style — active tab connects seamlessly to the card body below.
<VxTabs v-model="active" :items="items" variant="folder">
<template #panel-home>
<p>Home content here.</p>
</template>
<template #panel-profile>
<p>Profile content here.</p>
</template>
</VxTabs>Closable Tabs
Set closable: true on tab items. Listen to the close event to remove tabs.
<script setup>
import { ref } from 'vue'
import { VxTabs } from 'veloxui'
const active = ref('tab1')
const items = ref([
{ key: 'tab1', label: 'Welcome', closable: false },
{ key: 'tab2', label: 'Document.vue', closable: true },
{ key: 'tab3', label: 'Settings.ts', closable: true },
])
function closeTab(key) {
items.value = items.value.filter(t => t.key !== key)
if (active.value === key) active.value = items.value[0]?.key
}
</script>
<template>
<VxTabs v-model="active" :items="items" variant="card" @close="closeTab" />
</template>Sizes
Three sizes: sm, md (default), and lg.
<VxTabs :items="items" v-model="active" size="sm" />
<VxTabs :items="items" v-model="active" size="md" />
<VxTabs :items="items" v-model="active" size="lg" />Block Mode
Tabs stretch to fill full width equally.
<VxTabs v-model="active" :items="items" block />Content Panels
Use panel-{key} slots to render content that switches with tabs.
<VxTabs v-model="active" :items="items">
<template #panel-profile>
<VxInput placeholder="Display name" />
<VxButton size="sm">Save</VxButton>
</template>
<template #panel-password>
<VxInput placeholder="Current password" type="password" />
<VxInput placeholder="New password" type="password" />
<VxButton size="sm">Update</VxButton>
</template>
<template #panel-team>
<p>Invite and manage your team members here.</p>
</template>
</VxTabs>Vertical (Left / Right)
Use position="left" or position="right" for vertical tabs.
Manage your general preferences and configuration.
<VxTabs v-model="active" :items="items" position="left">
<template #panel-settings>
<p>General settings content...</p>
</template>
<template #panel-security>
<p>Security content...</p>
</template>
</VxTabs>Bottom Position
<VxTabs v-model="active" :items="items" position="bottom">
<template #panel-home>
<p>Home content with tabs at the bottom.</p>
</template>
</VxTabs>API
VxTabs Props
| Prop | Type | Default | Description |
|---|---|---|---|
v-model | string | — | Active tab key |
items | TabItem[] | [] | Tab items |
variant | 'line' | 'card' | 'pill' | 'segment' | 'folder' | 'line' | Visual style |
size | 'sm' | 'md' | 'lg' | 'md' | Tab size |
position | 'top' | 'bottom' | 'left' | 'right' | 'top' | Tab bar position |
block | boolean | false | Stretch tabs to equal width |
root-class | string | string[] | object | — | Class on root |
TabItem
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique identifier |
label | string | Yes | Tab label text |
icon | string | Component | No | Tab icon |
disabled | boolean | No | Disable this tab |
closable | boolean | No | Show close button |
badge | string | number | No | Counter shown next to the label. Numbers greater than 9 collapse to +9; the full value appears in a tooltip on hover |
fullBadge | boolean | No | Always show the full badge value without truncation (disables the +9 collapse) |
dot | boolean | No | Show a small pulse dot instead of a numeric badge |
Slots
| Slot | Props | Description |
|---|---|---|
tab | { item, active } | Custom tab button content for all tabs |
tab-{key} | { item, active } | Custom tab button content for a specific tab |
panel | { item, active } | Default panel content for all tabs |
panel-{key} | { item, active } | Panel content for a specific tab |
extra | — | Extra content after tabs in the nav bar |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | (key: string) | Active tab changed |
change | (key: string) | Tab selected |
close | (key: string) | Tab close button clicked |