Tab ​
A versatile component for organizing content into tabbed sections. It supports switching between views, navigation via links , and integration with routing for dynamic content rendering.
<script setup lang="ts">
import { ref } from 'vue'
import { HTabs, type IconName } from '@holistics/design-system'
const tabs = [{ id: 'tab1', label: 'Tab 1', icon: 'user' as IconName }, { id: 'tab2', label: 'Tab 2' }, { id: 'tab3', label: 'Tab 3' }]
const orientation = 'horizontal'
const selected = ref('tab2')
</script>
<template>
<HTabs
v-model:selected="selected"
:tabs
:orientation
class="h-52 w-96"
>
<template #tab1>
<div class="h-full bg-blue-100">
Tab content 1
</div>
</template>
<template #tab2>
<div class="h-full bg-green-100">
Tab content 2
</div>
</template>
<template #tab3>
<div class="h-full bg-red-100">
Tab content 3
</div>
</template>
</HTabs>
</template>Examples ​
Horizontal ​
<script setup lang="ts">
import { ref } from 'vue'
import { HTabs, type IconName } from '@holistics/design-system'
const tabs = [{ id: 'tab1', label: 'Tab 1', icon: 'user' as IconName }, { id: 'tab2', label: 'Tab 2' }, { id: 'tab3', label: 'Tab 3' }]
const orientation = 'horizontal'
const selected = ref('tab2')
</script>
<template>
<HTabs
v-model:selected="selected"
:tabs
:orientation
class="h-52 w-96"
>
<template #tab1>
<div class="h-full bg-blue-100">
Tab content 1
</div>
</template>
<template #tab2>
<div class="h-full bg-green-100">
Tab content 2
</div>
</template>
<template #tab3>
<div class="h-full bg-red-100">
Tab content 3
</div>
</template>
</HTabs>
</template>Vertical ​
<script setup lang="ts">
import { ref } from 'vue'
import { HTabs, type IconName } from '@holistics/design-system'
const tabs = [{ id: 'tab1', label: 'Tab 1', icon: 'user' as IconName }, { id: 'tab2', label: 'Tab 2' }, { id: 'tab3', label: 'Tab 3' }]
const orientation = 'vertical'
const selected = ref('tab2')
</script>
<template>
<HTabs
v-model:selected="selected"
:tabs
:orientation
class="h-52 w-96"
>
<template #tab1>
<div class="h-full bg-blue-100">
Tab content 1
</div>
</template>
<template #tab2>
<div class="h-full bg-green-100">
Tab content 2
</div>
</template>
<template #tab3>
<div class="h-full bg-red-100">
Tab content 3
</div>
</template>
</HTabs>
</template>Fill width ​
<script setup lang="ts">
import { ref } from 'vue'
import { HTabs, type IconName } from '@holistics/design-system'
const tabs = [{ id: 'tab1', label: 'Tab 1', icon: 'user' as IconName }, { id: 'tab2', label: 'Tab 2' }, { id: 'tab3', label: 'Tab 3' }]
const orientation = 'horizontal'
const selected = ref('tab2')
</script>
<template>
<HTabs
v-model:selected="selected"
:tabs
:orientation
fill-width
class="h-52 w-96"
>
<template #tab1>
<div class="h-full bg-blue-100">
Tab content 1
</div>
</template>
<template #tab2>
<div class="h-full bg-green-100">
Tab content 2
</div>
</template>
<template #tab3>
<div class="h-full bg-red-100">
Tab content 3
</div>
</template>
</HTabs>
</template>Content overflow ​
HTabs does not manage the overflow of its content. When a tab's content is larger than the available space, the callsite must handle it manually — for example by adding overflow-auto on the slot content.
To apply the same overflow handling to every tab at once, pass h-full overflow-auto to the tabPanelClass prop instead of adding it on each slot.
<script setup lang="ts">
import { ref } from 'vue'
import { HTabs, type IconName } from '@holistics/design-system'
const tabs = [{ id: 'tab1', label: 'Tab 1', icon: 'user' as IconName }, { id: 'tab2', label: 'Tab 2' }, { id: 'tab3', label: 'Tab 3' }]
const orientation = 'horizontal'
const selected = ref('tab1')
const longText = Array.from({ length: 20 }, (_, i) => `Line ${i + 1}: content that overflows the tab panel.`)
</script>
<template>
<!--
HTabs does not manage content overflow itself. When the content is taller
than the panel, the callsite must handle it. Passing `h-full overflow-auto`
to `tabPanelClass` applies the overflow handling to every tab at once,
instead of adding it on each individual slot.
-->
<HTabs
v-model:selected="selected"
:tabs
:orientation
tab-panel-class="h-full overflow-auto"
class="h-52 w-96"
>
<template #tab1>
<div class="bg-blue-100">
<p
v-for="line in longText"
:key="line"
>
{{ line }}
</p>
</div>
</template>
<template #tab2>
<div class="h-full bg-green-100">
Tab content 2
</div>
</template>
<template #tab3>
<div class="h-full bg-red-100">
Tab content 3
</div>
</template>
</HTabs>
</template>API ​
Pass-through: <div> ​
What does this mean?
All props, events, and attrs that are not specified in the tables below will be passed to the element/component described above.
Props ​
| Name | Type | Description |
|---|---|---|
tabs * | TabProps[] | List of tabs to render |
orientation | "vertical" | "horizontal"= "horizontal" | Layout direction of the tab list |
fillWidth | boolean | Stretches tabs to fill the available width |
border | boolean= true | Shows the bottom border under the tab list (horizontal orientation only) |
tabListClass | HTMLAttributeClass | Applied to the tab list element |
tabIndicatorClass | HTMLAttributeClass | Applied to the active tab indicator element |
tabPanelClass | HTMLAttributeClass | Applied to the tab panel element |
selected | string |
Events ​
| Name | Parameters | Description |
|---|---|---|
@update:selected | [value: string | undefined] |