Tabs
Switch between related views of the same thing — a teacher’s about, classes and reviews; a festival’s days — without leaving the page.
Demo
Lisandra grew up dancing casino in Havana and has taught Cuban salsa in Munich since 2015. Her classes are playful, musical and big on connection.
<Tabs default-value="about">
<TabsList>
<TabsTrigger value="about">About</TabsTrigger>
<TabsTrigger value="classes">Classes</TabsTrigger>
<TabsTrigger value="reviews">Reviews</TabsTrigger>
</TabsList>
<TabsContent value="about">…</TabsContent>
<TabsContent value="classes">…</TabsContent>
<TabsContent value="reviews">…</TabsContent>
</Tabs>Controlled, full width
Bind v-model when the active tab matters elsewhere (URL, analytics). Stretch the list with grid on phones so every target is wide.
- 19:00Welcome ruedaMain hall
- 22:00Opening party — DJ AlöshaMain hall
Active: fri
<script setup lang="ts">
const day = ref('fri')
</script>
<template>
<Tabs v-model="day">
<TabsList class="w-full grid grid-cols-3">
<TabsTrigger value="fri">Fri</TabsTrigger>
<TabsTrigger value="sat">Sat</TabsTrigger>
<TabsTrigger value="sun">Sun</TabsTrigger>
</TabsList>
<TabsContent v-for="(slots, d) in schedule" :key="d" :value="d">
<!-- the day's sessions -->
</TabsContent>
</Tabs>
</template>States
Active tabs lift onto the background with a soft shadow. Disabled tabs fade and are skipped by the keyboard.
<TabsTrigger value="videos" disabled>Videos</TabsTrigger>Props
Parts wrap reka-ui Tabs; their props pass straight through.
| Prop | Type | Default | Description |
|---|---|---|---|
| modelValue / v-model | string | number | — | Tabs (root). The active tab’s value, controlled. |
| defaultValue | string | number | — | Tabs (root). Initial tab when uncontrolled. |
| orientation | 'horizontal' | 'vertical' | 'horizontal' | Tabs (root). Sets arrow-key direction and aria-orientation. TabsList styles are horizontal — add flex-col yourself for vertical. |
| activationMode | 'automatic' | 'manual' | 'automatic' | Tabs (root). Automatic switches on arrow focus; manual waits for Enter/Space. Use manual if a panel is slow to render. |
| value | string | number | — | TabsTrigger, TabsContent. Required. Pairs a trigger with its panel. |
| disabled | boolean | false | TabsTrigger. Greys out (opacity-50) and skips it in keyboard order. |
| forceMount | boolean | — | TabsContent. Keep the panel in the DOM when hidden (e.g. to preserve a form’s state). |
| loop | boolean | true | TabsList. Arrow keys wrap from the last tab to the first. |
| class | HTMLAttributes["class"] | — | TabsList, TabsTrigger, TabsContent. Merged with cn(). |
Do & don't
Do — Two to five short, parallel labels for views of the same thing.
Don't — Use tabs as site navigation or crowd in so many that they scroll off a phone screen.
Accessibility
- reka-ui renders the WAI-ARIA tabs pattern:
role="tablist",role="tab"witharia-selectedandaria-controls, androle="tabpanel"labelled by its tab. - Keyboard: Tab enters the list on the active tab, ←/→ move between tabs, Home/End jump to the ends. Tab again moves into the panel.
- Tab labels must be text, not icons alone. If a label truncates at 375px (TabsTrigger truncates long labels), shorten it — “Sat” not “Saturday 24 May”.
- Tabs hide content. Don’t use them for content people need to compare side by side, or for steps that must happen in order — use a stepper or the page itself.