Tabs
A set of layered sections of content—known as tab panels—that are displayed one at a time.
Examples
Basic
Prop | Default | Type | Description |
---|---|---|---|
content | - | string | Set the tooltip content. |
disabled | - | boolean | Set to disable the tooltip. |
Preview
Code
We'll never share your email with anyone else.
<script setup lang="ts">
const items = ref([
{
value: 'account',
name: 'Account',
},
{
value: 'service',
name: 'Service Provider',
},
{
value: 'reviews',
name: 'Reviews',
},
])
</script>
<template>
<NTabs
:items="items"
default-value="account"
>
<template #content="{ item }">
<div v-if="item.value === 'account'" class="flex flex-col items-start">
<NFormGroup
label="Email"
required
description="We'll never share your email with anyone else."
>
<NInput
placeholder="phojrengel@gmail.com"
leading="i-heroicons-envelope-20-solid"
/>
</NFormGroup>
<NButton class="mt-3 self-start px-4">
Confirm
</NButton>
</div>
<div v-if="item.value === 'service'">
<ExampleVueRadioFormGroup />
</div>
<div v-if="item.value === 'reviews'">
<NFormGroup label="Write your review">
<NInput
type="textarea"
placeholder="Write your review here..."
resize="x"
/>
</NFormGroup>
<NButton class="mt-2 self-start px-4">
Save changes
</NButton>
</div>
</template>
</NTabs>
</template>
Read more in Radix Tabs Root API.
Variant and Color
Prop | Default | Type | Description |
---|---|---|---|
tabs | soft-black | string | Set the tabs variant and color. |
_tabsTrigger.tabs | - | string | Set the tabs variant and color via _tabsTrigger . |
Preview
Code
We'll never share your email with anyone else.
<script setup lang="ts">
const items = ref([
{
value: 'account',
name: 'Account',
tabs: 'solid-lime',
_tabsTrigger: {
leading: 'i-heroicons-user-20-solid',
},
},
{
value: 'service',
name: 'Service Provider',
_tabsTrigger: {
tabs: 'soft-pink',
trailing: 'i-heroicons-chevron-right-20-solid',
},
},
{
value: 'reviews',
name: 'Reviews',
_tabsTrigger: {
tabs: 'outline-success',
},
},
])
</script>
<template>
<NTabs
:items="items"
default-value="account"
>
<template #content="{ item }">
<div v-if="item.value === 'account'" class="flex flex-col items-start">
<NFormGroup
label="Email"
required
description="We'll never share your email with anyone else."
>
<NInput
placeholder="phojrengel@gmail.com"
leading="i-heroicons-envelope-20-solid"
/>
</NFormGroup>
<NButton class="mt-3 self-start px-4">
Confirm
</NButton>
</div>
<div v-if="item.value === 'service'">
<ExampleVueRadioFormGroup />
</div>
<div v-if="item.value === 'reviews'">
<NFormGroup label="Write your review">
<NInput
type="textarea"
placeholder="Write your review here..."
resize="x"
/>
</NFormGroup>
<NButton class="mt-2 self-start px-4">
Save changes
</NButton>
</div>
</template>
</NTabs>
</template>
Read more in Button Colors
Disabled
Prop | Default | Type | Description |
---|---|---|---|
disabled | false | boolean | Set the tabs disabled. |
_tabsTrigger.disabled | - | boolean | Set the tabs disabled via _tabsTrigger . |
Preview
Code
or
<script setup lang="ts">
const items = ref([
{
value: 'tab1',
name: 'Tab 1',
disabled: false,
},
{
value: 'tab2',
name: 'Tab 2',
_tabsTrigger: {
disabled: true,
},
},
{
value: 'tab3',
name: 'Tab 3',
},
{
value: 'tab4',
name: 'Tab 4',
disabled: true,
},
])
</script>
<template>
<div>
<NTabs
:items="items"
disabled
default-value="tab1"
>
<template #trigger="{ disabled }">
Disabled: {{ disabled }}
</template>
</NTabs>
<NSeparator label="or" />
<NTabs
:items="items"
default-value="tab3"
>
<template #trigger="{ disabled }">
Disabled: {{ disabled }}
</template>
</NTabs>
</div>
</template>
Size
Prop | Default | Type | Description |
---|---|---|---|
size | - | string | Set the tabs size. |
_tabsTrigger.size | - | string | Set the trigger size. |
_tabsContent.size | - | string | Set the content size. |
Preview
Code
Tab 1 content
Tab 2 content
<script setup lang="ts">
const items = ref([
{
value: 'tab1',
name: 'Una Tab 1',
content: 'Tab 1 content',
disabled: false,
},
{
value: 'tab2',
name: 'Una Tab 2',
content: 'Tab 2 content',
disabled: false,
},
{
value: 'tab3',
name: 'Una Tab 3',
content: 'Tab 3 content',
disabled: false,
},
])
</script>
<template>
<div class="space-y-8">
<NTabs :items="items" default-value="tab1" size="md" class="mb-4" />
<NTabs
:items="items" default-value="tab2" size="xl" :_tabs-trigger="{
size: 'xl',
}"
/>
</div>
</template>
Read more in Button Size
Slots
Name | Props | Description |
---|---|---|
list | items | The list slot. |
trigger | - | The trigger slot. |
content | item | The content slot. |
Presets
shortcuts/tabs.ts
type TabsPrefix = 'tabs'
export const staticTabs: Record<`${TabsPrefix}-${string}` | TabsPrefix, string> = {
// configurations
'tabs': 'transition-colors duration-200 ease-out',
'tabs-default-variant': 'tabs-soft-black',
'tabs-disabled': 'n-disabled',
// components
'tabs-root': 'flex flex-col w-full',
'tabs-trigger': 'w-full focus-visible:z-10',
'tabs-list': 'flex bg-muted items-center justify-center rounded-md p-1 w-full',
'tabs-content': 'mt-4 text-base',
}
export const dynamicTabs = [
[/^tabs-([^-]+)-([^-]+)$/, ([, v = 'solid', c = 'primary']) => `data-[state=active]:btn-${v}-${c}`],
]
export const tabs = [
...dynamicTabs,
staticTabs,
]
Props
types/tabs.ts
import type { TabsContentProps, TabsListProps, TabsRootProps, TabsTriggerProps } from 'radix-vue'
import type { HTMLAttributes } from 'vue'
import type { NButtonProps } from './button'
interface BaseExtensions {
class?: HTMLAttributes['class']
size?: HTMLAttributes['class']
}
export interface NTabsProps extends TabsRootProps, BaseExtensions, Pick<NTabsTriggerProps, 'tabs' | 'disabled'> {
/**
* The array of items that is passed to tabs.
*
* @default []
*/
items: any[]
// sub-components
_tabsContent?: Partial<NTabsContentProps>
_tabsTrigger?: Partial<NTabsTriggerProps>
_tabsList?: Partial<NTabsListProps>
}
export interface NTabsRootProps extends TabsRootProps, BaseExtensions {
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/tabs.ts
*/
una?: {
// components
tabsRoot?: HTMLAttributes['class']
}
}
export interface NTabsListProps extends TabsListProps, BaseExtensions {
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/tabs.ts
*/
una?: {
// components
tabsList?: HTMLAttributes['class']
}
}
export interface NTabsTriggerProps extends TabsTriggerProps, Omit<NButtonProps, 'una' | 'size'>, BaseExtensions {
/**
* Allows you to add `UnaUI` button preset properties,
* Think of it as a shortcut for adding options or variants to the preset if available.
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/tabs.ts
* @example
* tabs="solid-green"
*/
tabs?: string
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/tabs.ts
*/
una?: {
// components
tabsTrigger?: HTMLAttributes['class']
tabsDefaultVariant?: HTMLAttributes['class']
} & NButtonProps['una']
}
export interface NTabsContentProps extends TabsContentProps, BaseExtensions {
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/tabs.ts
*/
una?: {
// components
tabsContent?: HTMLAttributes['class']
}
}
Components
Tabs.vue
TabsRoot.vue
TabsList.vue
TabsTrigger.vue
TabsContent.vue
<script setup lang="ts">
import type { TabsRootEmits } from 'radix-vue'
import type { NTabsProps } from '../../../types/tabs'
import { useForwardPropsEmits } from 'radix-vue'
import { computed } from 'vue'
import { omitProps } from '../../../utils'
import TabsContent from './TabsContent.vue'
import TabsList from './TabsList.vue'
import TabsRoot from './TabsRoot.vue'
import TabsTrigger from './TabsTrigger.vue'
const props = defineProps<NTabsProps>()
const emits = defineEmits<TabsRootEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<TabsRoot
v-bind="omitProps(forwarded, ['items', 'tabs', 'disabled'])"
:default-value="defaultValue"
>
<TabsList v-bind="forwarded._tabsList">
<slot name="list" :items="items">
<template
v-for="item in items"
:key="item.value"
>
<TabsTrigger
:tabs="item?._tabsTrigger?.tabs || item.tabs || props.tabs"
:disabled="item?._tabsTrigger?.disabled ?? item.disabled ?? props.disabled"
:value="item.value"
v-bind="{ ...forwarded._tabsTrigger, ...item?._tabsTrigger }"
>
<slot name="trigger" :item="item" :disabled="item?._tabsTrigger?.disabled ?? item.disabled ?? props.disabled ?? false">
{{ item.name }}
</slot>
</TabsTrigger>
</template>
</slot>
</TabsList>
<template
v-for="item in items"
:key="item.value"
>
<TabsContent v-bind="forwarded._tabsContent" :value="item.value">
<slot name="content" :item="item">
<component :is="typeof item.content === 'string' ? 'span' : item.content">
{{ typeof item.content === 'string' ? item.content : '' }}
</component>
</slot>
</TabsContent>
</template>
</TabsRoot>
</template>
<script setup lang="ts">
import type { TabsRootEmits } from 'radix-vue'
import type { NTabsRootProps } from '../../../types/tabs'
import { TabsRoot, useForwardPropsEmits } from 'radix-vue'
import { cn } from '../../../utils'
const props = defineProps<NTabsRootProps>()
const emits = defineEmits<TabsRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<TabsRoot
v-bind="forwarded"
:class="cn(
'tabs-root',
props.class,
props.una?.tabsRoot,
)"
>
<slot />
</TabsRoot>
</template>
<script setup lang="ts">
import type { NTabsListProps } from '../../../types/tabs'
import { TabsList } from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
const props = defineProps<NTabsListProps>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<TabsList
v-bind="delegatedProps"
:class="cn(
'tabs-list',
props.class,
props.una?.tabsList,
)"
>
<slot />
</TabsList>
</template>
<script setup lang="ts">
import type { NTabsTriggerProps } from '../../../types/tabs'
import { TabsTrigger } from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
import Button from '../Button.vue'
const props = defineProps<NTabsTriggerProps>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<TabsTrigger
v-bind="delegatedProps"
:class="cn(
'tabs-trigger',
props.class,
)"
:una="{
...props.una,
btnDefaultVariant: props.tabs ? `tabs-${props.tabs}` : 'tabs-default-variant',
}"
:as="Button"
>
<slot />
</TabsTrigger>
</template>
<script setup lang="ts">
import type { NTabsContentProps } from '../../../types/tabs'
import { TabsContent } from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<NTabsContentProps>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<TabsContent
v-bind="{ ...delegatedProps, ...$attrs }"
:class="cn(
'tabs-content',
props.class,
props.una?.tabsContent,
)"
>
<slot />
</TabsContent>
</template>