Tabs
Switch between sections of the same screen without showing them all at once.
Anna Petrova
Second factor is off
Email
anna@northwind.example
Language
English
<script setup lang="ts">import { ref } from 'vue'import { UiTabs, UiListItem, UiAvatar, UiList, UiSwitch } from 'grani' const pane = ref('overview') const twoFactor = ref(false) const SESSIONS = [ { title: 'MacBook Pro, Lisbon', when: 'This device' }, { title: 'iPhone 15, Lisbon', when: '2 hours ago' }, { title: 'Firefox on Windows, Porto', when: '3 days ago' },]</script> <template> <UiTabs v-model="pane" class="w-full max-w-md" ariaLabel="Account" :tabs="[ { value: 'overview', label: 'Overview' }, { value: 'security', label: 'Security', badge: '!', badgeTone: 'danger', badgeLabel: 'needs attention', }, { value: 'sessions', label: 'Sessions', badge: 3, badgeLabel: '3 active' }, ]" > <template #header> <UiListItem title="Anna Petrova" description="Second factor is off"> <template #media><UiAvatar name="Anna Petrova" size="md" /></template> </UiListItem> </template> <template #overview> <UiList> <UiListItem title="Email" description="anna@northwind.example" /> <UiListItem title="Language" description="English" /> </UiList> </template> <template #security> <UiList> <UiListItem as="label" title="Second factor" description="A code from an app at every sign-in." > <template #actions><UiSwitch v-model="twoFactor" /></template> </UiListItem> </UiList> </template> <template #sessions> <UiList> <UiListItem v-for="d in SESSIONS" :key="d.title" :title="d.title" :description="d.when" /> </UiList> </template> </UiTabs></template>
Also called tab bar, tab strip, tabbed navigation.
Usage
<UiTabs v-model="value" :tabs="tabs" :aria-label="ariaLabel"/>
With GraniResolver in the Vite config, a template needs no import: set it up once.
API
| Name · type | Default | Description |
|---|---|---|
| modelValue*TabValue | Value of the active tab (v-model). | |
| tabs*TabItem[] | Tabs in order: value, label, disabled, and a badge with its badgeTone and badgeLabel (what it means, in words). | |
| ariaLabel*string | Accessible name of the tab set. | |
| variantpanel | underline= "panel" | "panel" | panel (the default) is the header of a block inside the card shell; underline is a strip on its own. |
| sizexs | sm | md | Step of a tab on the control ladder: sm 36px, md 44px. Unset, the app's size (createGrani({ size })); xs is read as sm. |
Anything else lands on the root element: native attributes, aria-*, data-testid.
When to use another component
| When you need | Take |
|---|---|
| A choice with no panes | Radio group segmented |
| Pages of one set | Pagination |
| The places of the app | App shell |
| A block shown or hidden in place | Reveal |