Usage
Items
Use theitems
prop as an array of objects with the following properties:
label?: string
icon?: string
avatar?: AvatarProps
content?: string
value?: string | number
disabled?: boolean
slot?: string
<script setup lang="ts">const items= ref([ { label: 'Account', icon: 'i-lucide-user', content: 'This is the account content.' }, { label: 'Password', icon: 'i-lucide-lock', content: 'This is the password content.' }])</script><template> <UTabs :items="items" class="w-full" /></template>
Content
Set thecontent
prop tofalse
to turn the Tabs into a toggle-only control without displaying any content. Defaults totrue
.
<script setup lang="ts">const items= ref([ { label: 'Account', icon: 'i-lucide-user', content: 'This is the account content.' }, { label: 'Password', icon: 'i-lucide-lock', content: 'This is the password content.' }])</script><template> <UTabs :content="false" :items="items" class="w-full" /></template>
Unmount
Use theunmount-on-hide
prop to prevent the content from being unmounted when the Tabs is collapsed. Defaults totrue
.
<script setup lang="ts">const items= ref([ { label: 'Account', icon: 'i-lucide-user', content: 'This is the account content.' }, { label: 'Password', icon: 'i-lucide-lock', content: 'This is the password content.' }])</script><template> <UTabs :unmount-on-hide="false" :items="items" class="w-full" /></template>
Color
Use thecolor
prop to change the color of the Tabs.
<script setup lang="ts">const items= ref([ { label: 'Account' }, { label: 'Password' }])</script><template> <UTabs color="neutral" :content="false" :items="items" class="w-full" /></template>
Variant
Use thevariant
prop to change the variant of the Tabs.
<script setup lang="ts">const items= ref([ { label: 'Account' }, { label: 'Password' }])</script><template> <UTabs color="neutral" variant="link" :content="false" :items="items" class="w-full" /></template>
Size
Use thesize
prop to change the size of the Tabs.
<script setup lang="ts">const items= ref([ { label: 'Account' }, { label: 'Password' }])</script><template> <UTabs size="md" variant="pill" :content="false" :items="items" class="w-full" /></template>
Orientation
Use theorientation
prop to change the orientation of the Tabs. Defaults tohorizontal
.
<script setup lang="ts">const items= ref([ { label: 'Account' }, { label: 'Password' }])</script><template> <UTabs orientation="vertical" variant="pill" :content="false" :items="items" class="w-full" /></template>
Examples
Control active item
You can control the active item by using thedefault-value
prop or thev-model
directive with the index of the item.
<script setup lang="ts">const items= [ { label: 'Account' }, { label: 'Password' }]const active= ref('0')// Note: This is for demonstration purposes only. Don't do this at home.onMounted(() => { setInterval(() => { active.value = String((Number(active.value)+ 1)% items.length) }, 2000)})</script><template> <UTabs v-model="active" :content="false" :items="items" class="w-full" /></template>
value
of one of the items if provided.With content slot
Use the#content
slot to customize the content of each item.
This is the Account tab.
<script setup lang="ts">const items= [ { label: 'Account', icon: 'i-lucide-user' }, { label: 'Password', icon: 'i-lucide-lock' }]</script><template> <UTabs :items="items" class="w-full"> <template #content="{ item}"> <p>This is the {{ item.label }} tab.</p> </template> </UTabs></template>
With custom slot
Use theslot
property to customize a specific item.
Make changes to your account here. Click save when you're done.
<script setup lang="ts">const items= [ { label: 'Account', description: 'Make changes to your account here. Click save when you\'re done.', icon: 'i-lucide-user', slot: 'account' }, { label: 'Password', description: 'Change your password here. After saving, you\'ll be logged out.', icon: 'i-lucide-lock', slot: 'password' }]const state= reactive({ name: 'Benjamin Canac', username: 'benjamincanac', currentPassword: '', newPassword: '', confirmPassword: ''})</script><template> <UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }"> <template #account="{ item}"> <p class="text-(--ui-text-muted) mb-4"> {{ item.description }} </p> <UForm :state="state" class="flex flex-col gap-4"> <UFormField label="Name" name="name"> <UInput v-model="state.name" class="w-full" /> </UFormField> <UFormField label="Username" name="username"> <UInput v-model="state.username" class="w-full" /> </UFormField> <UButton label="Save changes" type="submit" variant="soft" class="self-end" /> </UForm> </template> <template #password="{ item}"> <p class="text-(--ui-text-muted) mb-4"> {{ item.description }} </p> <UForm :state="state" class="flex flex-col gap-4"> <UFormField label="Current Password" name="current" required> <UInput v-model="state.currentPassword" type="password" required class="w-full" /> </UFormField> <UFormField label="New Password" name="new" required> <UInput v-model="state.newPassword" type="password" required class="w-full" /> </UFormField> <UFormField label="Confirm Password" name="confirm" required> <UInput v-model="state.confirmPassword" type="password" required class="w-full" /> </UFormField> <UButton label="Change password" type="submit" variant="soft" class="self-end" /> </UForm> </template> </UTabs></template>
API
Props
Prop | Default | Type |
---|---|---|
as |
|
The element or component this component should render as. |
items |
| |
color |
|
|
variant |
|
|
size |
|
|
orientation |
|
The orientation of the tabs. |
content |
|
The content of the tabs, can be disabled to prevent rendering the content. |
labelKey |
|
The key used to get the label from the item. |
defaultValue |
|
The value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs |
modelValue |
The controlled value of the tab to activate. Can be bind as | |
activationMode |
|
Whether a tab is activated automatically (on focus) or manually (on click). |
unmountOnHide |
|
When |
ui |
|
Slots
Slot | Type |
---|---|
leading |
|
default |
|
trailing |
|
content |
|
Emits
Event | Type |
---|---|
update:modelValue |
|
Theme
export default defineAppConfig({ ui: { tabs: { slots: { root: 'flex items-center gap-2', list: 'relative flex p-1 group', indicator: 'absolute transition-[translate,width] duration-200', trigger: [ 'group relative inline-flex items-center shrink-0 min-w-0 data-[state=inactive]:text-(--ui-text-muted) hover:data-[state=inactive]:not-disabled:text-(--ui-text) font-medium rounded-[calc(var(--ui-radius)*1.5)] disabled:cursor-not-allowed disabled:opacity-75 focus:outline-hidden', 'transition-colors' ], content: 'focus:outline-none w-full', leadingIcon: 'shrink-0', leadingAvatar: 'shrink-0', leadingAvatarSize: '', label: 'truncate' }, variants: { color: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, variant: { pill: { list: 'bg-(--ui-bg-elevated) rounded-[calc(var(--ui-radius)*2)]', trigger: 'flex-1 w-full', indicator: 'rounded-[calc(var(--ui-radius)*1.5)] shadow-xs' }, link: { list: 'border-(--ui-border)', indicator: 'rounded-full' } }, orientation: { horizontal: { root: 'flex-col', list: 'w-full', indicator: 'left-0 w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position)', trigger: 'justify-center' }, vertical: { list: 'flex-col', indicator: 'top-0 h-(--reka-tabs-indicator-size) translate-y-(--reka-tabs-indicator-position)' } }, size: { xs: { trigger: 'px-2 py-1 text-xs gap-1', leadingIcon: 'size-4', leadingAvatarSize: '3xs' }, sm: { trigger: 'px-2.5 py-1.5 text-xs gap-1.5', leadingIcon: 'size-4', leadingAvatarSize: '3xs' }, md: { trigger: 'px-3 py-1.5 text-sm gap-1.5', leadingIcon: 'size-5', leadingAvatarSize: '2xs' }, lg: { trigger: 'px-3 py-2 text-sm gap-2', leadingIcon: 'size-5', leadingAvatarSize: '2xs' }, xl: { trigger: 'px-3 py-2 text-base gap-2', leadingIcon: 'size-6', leadingAvatarSize: 'xs' } } }, compoundVariants: [ { orientation: 'horizontal', variant: 'pill', class: { indicator: 'inset-y-1' } }, { orientation: 'horizontal', variant: 'link', class: { list: 'border-b -mb-px', indicator: '-bottom-px h-px' } }, { orientation: 'vertical', variant: 'pill', class: { indicator: 'inset-x-1', list: 'items-center' } }, { orientation: 'vertical', variant: 'link', class: { list: 'border-s -ms-px', indicator: '-start-px w-px' } }, { color: 'primary', variant: 'pill', class: { indicator: 'bg-(--ui-primary)', trigger: 'data-[state=active]:text-(--ui-bg) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-primary)' } }, { color: 'neutral', variant: 'pill', class: { indicator: 'bg-(--ui-bg-inverted)', trigger: 'data-[state=active]:text-(--ui-bg) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-border-inverted)' } }, { color: 'primary', variant: 'link', class: { indicator: 'bg-(--ui-primary)', trigger: 'data-[state=active]:text-(--ui-primary) focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-primary)' } }, { color: 'neutral', variant: 'link', class: { indicator: 'bg-(--ui-bg-inverted)', trigger: 'data-[state=active]:text-(--ui-text-highlighted) focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-border-inverted)' } } ], defaultVariants: { color: 'primary', variant: 'pill', size: 'md' } } }})
import { defineConfig } from 'vite'import vuefrom '@vitejs/plugin-vue'import uifrom '@nuxt/ui/vite'export default defineConfig({ plugins: [ vue(), ui({ ui: { tabs: { slots: { root: 'flex items-center gap-2', list: 'relative flex p-1 group', indicator: 'absolute transition-[translate,width] duration-200', trigger: [ 'group relative inline-flex items-center shrink-0 min-w-0 data-[state=inactive]:text-(--ui-text-muted) hover:data-[state=inactive]:not-disabled:text-(--ui-text) font-medium rounded-[calc(var(--ui-radius)*1.5)] disabled:cursor-not-allowed disabled:opacity-75 focus:outline-hidden', 'transition-colors' ], content: 'focus:outline-none w-full', leadingIcon: 'shrink-0', leadingAvatar: 'shrink-0', leadingAvatarSize: '', label: 'truncate' }, variants: { color: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, variant: { pill: { list: 'bg-(--ui-bg-elevated) rounded-[calc(var(--ui-radius)*2)]', trigger: 'flex-1 w-full', indicator: 'rounded-[calc(var(--ui-radius)*1.5)] shadow-xs' }, link: { list: 'border-(--ui-border)', indicator: 'rounded-full' } }, orientation: { horizontal: { root: 'flex-col', list: 'w-full', indicator: 'left-0 w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position)', trigger: 'justify-center' }, vertical: { list: 'flex-col', indicator: 'top-0 h-(--reka-tabs-indicator-size) translate-y-(--reka-tabs-indicator-position)' } }, size: { xs: { trigger: 'px-2 py-1 text-xs gap-1', leadingIcon: 'size-4', leadingAvatarSize: '3xs' }, sm: { trigger: 'px-2.5 py-1.5 text-xs gap-1.5', leadingIcon: 'size-4', leadingAvatarSize: '3xs' }, md: { trigger: 'px-3 py-1.5 text-sm gap-1.5', leadingIcon: 'size-5', leadingAvatarSize: '2xs' }, lg: { trigger: 'px-3 py-2 text-sm gap-2', leadingIcon: 'size-5', leadingAvatarSize: '2xs' }, xl: { trigger: 'px-3 py-2 text-base gap-2', leadingIcon: 'size-6', leadingAvatarSize: 'xs' } } }, compoundVariants: [ { orientation: 'horizontal', variant: 'pill', class: { indicator: 'inset-y-1' } }, { orientation: 'horizontal', variant: 'link', class: { list: 'border-b -mb-px', indicator: '-bottom-px h-px' } }, { orientation: 'vertical', variant: 'pill', class: { indicator: 'inset-x-1', list: 'items-center' } }, { orientation: 'vertical', variant: 'link', class: { list: 'border-s -ms-px', indicator: '-start-px w-px' } }, { color: 'primary', variant: 'pill', class: { indicator: 'bg-(--ui-primary)', trigger: 'data-[state=active]:text-(--ui-bg) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-primary)' } }, { color: 'neutral', variant: 'pill', class: { indicator: 'bg-(--ui-bg-inverted)', trigger: 'data-[state=active]:text-(--ui-bg) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-border-inverted)' } }, { color: 'primary', variant: 'link', class: { indicator: 'bg-(--ui-primary)', trigger: 'data-[state=active]:text-(--ui-primary) focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-primary)' } }, { color: 'neutral', variant: 'link', class: { indicator: 'bg-(--ui-bg-inverted)', trigger: 'data-[state=active]:text-(--ui-text-highlighted) focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-border-inverted)' } } ], defaultVariants: { color: 'primary', variant: 'pill', size: 'md' } } } }) ]})
import { defineConfig } from 'vite'import vuefrom '@vitejs/plugin-vue'import uiProfrom '@nuxt/ui-pro/vite'export default defineConfig({ plugins: [ vue(), uiPro({ ui: { tabs: { slots: { root: 'flex items-center gap-2', list: 'relative flex p-1 group', indicator: 'absolute transition-[translate,width] duration-200', trigger: [ 'group relative inline-flex items-center shrink-0 min-w-0 data-[state=inactive]:text-(--ui-text-muted) hover:data-[state=inactive]:not-disabled:text-(--ui-text) font-medium rounded-[calc(var(--ui-radius)*1.5)] disabled:cursor-not-allowed disabled:opacity-75 focus:outline-hidden', 'transition-colors' ], content: 'focus:outline-none w-full', leadingIcon: 'shrink-0', leadingAvatar: 'shrink-0', leadingAvatarSize: '', label: 'truncate' }, variants: { color: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, variant: { pill: { list: 'bg-(--ui-bg-elevated) rounded-[calc(var(--ui-radius)*2)]', trigger: 'flex-1 w-full', indicator: 'rounded-[calc(var(--ui-radius)*1.5)] shadow-xs' }, link: { list: 'border-(--ui-border)', indicator: 'rounded-full' } }, orientation: { horizontal: { root: 'flex-col', list: 'w-full', indicator: 'left-0 w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position)', trigger: 'justify-center' }, vertical: { list: 'flex-col', indicator: 'top-0 h-(--reka-tabs-indicator-size) translate-y-(--reka-tabs-indicator-position)' } }, size: { xs: { trigger: 'px-2 py-1 text-xs gap-1', leadingIcon: 'size-4', leadingAvatarSize: '3xs' }, sm: { trigger: 'px-2.5 py-1.5 text-xs gap-1.5', leadingIcon: 'size-4', leadingAvatarSize: '3xs' }, md: { trigger: 'px-3 py-1.5 text-sm gap-1.5', leadingIcon: 'size-5', leadingAvatarSize: '2xs' }, lg: { trigger: 'px-3 py-2 text-sm gap-2', leadingIcon: 'size-5', leadingAvatarSize: '2xs' }, xl: { trigger: 'px-3 py-2 text-base gap-2', leadingIcon: 'size-6', leadingAvatarSize: 'xs' } } }, compoundVariants: [ { orientation: 'horizontal', variant: 'pill', class: { indicator: 'inset-y-1' } }, { orientation: 'horizontal', variant: 'link', class: { list: 'border-b -mb-px', indicator: '-bottom-px h-px' } }, { orientation: 'vertical', variant: 'pill', class: { indicator: 'inset-x-1', list: 'items-center' } }, { orientation: 'vertical', variant: 'link', class: { list: 'border-s -ms-px', indicator: '-start-px w-px' } }, { color: 'primary', variant: 'pill', class: { indicator: 'bg-(--ui-primary)', trigger: 'data-[state=active]:text-(--ui-bg) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-primary)' } }, { color: 'neutral', variant: 'pill', class: { indicator: 'bg-(--ui-bg-inverted)', trigger: 'data-[state=active]:text-(--ui-bg) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-border-inverted)' } }, { color: 'primary', variant: 'link', class: { indicator: 'bg-(--ui-primary)', trigger: 'data-[state=active]:text-(--ui-primary) focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-primary)' } }, { color: 'neutral', variant: 'link', class: { indicator: 'bg-(--ui-bg-inverted)', trigger: 'data-[state=active]:text-(--ui-text-highlighted) focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-border-inverted)' } } ], defaultVariants: { color: 'primary', variant: 'pill', size: 'md' } } } }) ]})