Skip to content

Tab ​

Stable

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.

vue
<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 ​

vue
<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 ​

vue
<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 ​

vue
<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.

vue
<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 ​

NameTypeDescription
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 ​

NameParametersDescription
@update:selected
[value: string | undefined]