Component
Vue Tabs Component
<dom-tabs>Roving-tabindex tab list with managed ARIA, keyboard navigation, and panel linking.
Playground
Try every prop live
Tabs playground
Edit props in the inspector — switch tabs to preview panel slots and the active tab key.
Overview panel content.
Pricing panel content.
Changelog panel content.
Properties
Control props
Visual treatment for the tab list.
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { reactive } from 'vue';
import { DomTabs } from '@getdom/studio';
const data = reactive({
"modelValue": "overview",
"tabs": [
{
"key": "overview",
"label": "Overview"
},
{
"key": "pricing",
"label": "Pricing"
},
{
"key": "changelog",
"label": "Changelog"
}
],
"variant": "pill",
"fill": false
});
</script>
<template>
<DomTabs
v-bind="data"
@update:modelValue="data.modelValue = $event"
/>
</template>Demo
Live preview
Variant
Page tabs
Page tabs
Editor-style tabs can use variant="page", scoped tab labels, and reactive tab arrays.
src/app/dashboard/page.tsx
Page tabs sit flush with the panel, so they work well for editor files, browser-like workspaces, and record pages.
src/components/MissionControl.tsx
Use the tab slot to add status dots, icons, or compact metadata while keeping the headless tab behavior intact.
src/lib/agent-client.ts
The same component can switch between pill navigation and page-style workspace navigation with the variant prop.
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomButton, DomTabs } from '@getdom/studio';
const active = ref('dashboard-page');
const openTabs = ref([
{ key: 'dashboard-page', label: 'page.tsx', path: 'src/app/dashboard/page.tsx', status: 'modified' },
{ key: 'mission-control', label: 'MissionControl.tsx', path: 'src/components/MissionControl.tsx', status: 'added' },
{ key: 'agent-client', label: 'agent-client.ts', path: 'src/lib/agent-client.ts', status: '' },
]);
const availableTabs = [
{ key: 'dashboard-page', label: 'page.tsx', path: 'src/app/dashboard/page.tsx', status: 'modified' },
{ key: 'mission-control', label: 'MissionControl.tsx', path: 'src/components/MissionControl.tsx', status: 'added' },
{ key: 'agent-client', label: 'agent-client.ts', path: 'src/lib/agent-client.ts', status: '' },
{ key: 'task-card', label: 'TaskCard.tsx', path: 'src/components/TaskCard.tsx', status: 'modified' },
];
function openTab(tab) {
if (!openTabs.value.some((item) => item.key === tab.key)) openTabs.value.push(tab);
active.value = tab.key;
}
function statusClass(status) {
if (status === 'added') return 'bg-success';
if (status === 'modified') return 'bg-warning';
return 'bg-muted-fg/40';
}
</script>
<template>
<div class="w-full max-w-3xl overflow-hidden rounded-lg border border-border bg-canvas shadow-sm">
<DomTabs v-model="active" :tabs="openTabs" variant="page">
<template #tab="{ tab }">
<span class="size-2 shrink-0 rounded-full" :class="statusClass(tab.status)"></span>
<span class="min-w-0 truncate">{{ tab.label }}</span>
</template>
<template #dashboard-page>
<div class="p-5 text-sm text-muted-fg">
<p class="font-mono text-xs text-primary">src/app/dashboard/page.tsx</p>
<p class="mt-3 leading-6">Page tabs sit flush with the panel, so they work well for editor files, browser-like workspaces, and record pages.</p>
</div>
</template>
<template #mission-control>
<div class="p-5 text-sm text-muted-fg">
<p class="font-mono text-xs text-primary">src/components/MissionControl.tsx</p>
<p class="mt-3 leading-6">Use the tab slot to add status dots, icons, or compact metadata while keeping the headless tab behavior intact.</p>
</div>
</template>
<template #agent-client>
<div class="p-5 text-sm text-muted-fg">
<p class="font-mono text-xs text-primary">src/lib/agent-client.ts</p>
<p class="mt-3 leading-6">The same component can switch between pill navigation and page-style workspace navigation with the variant prop.</p>
</div>
</template>
<template #task-card>
<div class="p-5 text-sm text-muted-fg">
<p class="font-mono text-xs text-primary">src/components/TaskCard.tsx</p>
<p class="mt-3 leading-6">Tabs can be added from reactive arrays as users open new pages or files.</p>
</div>
</template>
</DomTabs>
<div class="flex flex-wrap gap-2 border-t border-border bg-secondary/40 p-3">
<DomButton
v-for="tab in availableTabs"
:key="tab.key"
size="sm"
variant="secondary"
@click="openTab(tab)"
>
Open {{ tab.label }}
</DomButton>
</div>
</div>
</template>
Variant
Flat tabs
Flat tabs
Flat tabs use a simple active underline for page-level navigation without a contained control surface.
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomTabs } from '@getdom/studio';
const active = ref('activity');
const tabs = [
{ key: 'activity', label: 'Activity' },
{ key: 'analytics', label: 'Analytics' },
{ key: 'settings', label: 'Settings' },
];
</script>
<template>
<div class="w-full max-w-2xl">
<DomTabs v-model="active" :tabs="tabs" variant="flat">
<template #activity>
<div class="rounded-xl border border-border bg-canvas p-5 text-sm text-muted-fg">
Recent workspace activity appears here.
</div>
</template>
<template #analytics>
<div class="rounded-xl border border-border bg-canvas p-5 text-sm text-muted-fg">
Analytics and performance trends appear here.
</div>
</template>
<template #settings>
<div class="rounded-xl border border-border bg-canvas p-5 text-sm text-muted-fg">
Workspace settings appear here.
</div>
</template>
</DomTabs>
</div>
</template>
Usage
Vue
<script setup>
import { DomTabs } from '@getdom/studio/vue';
import { ref } from 'vue';
const active = ref('overview');
const tabs = [
{ key: 'overview', label: 'Overview' },
{ key: 'pricing', label: 'Pricing' },
];
</script>
<template>
<DomTabs v-model="active" :tabs="tabs">
<template #overview>Overview content…</template>
<template #pricing>Pricing content…</template>
</DomTabs>
</template>Usage
Web component
<dom-tabs value="overview">
<div role="tablist">
<button data-tab="overview">Overview</button>
<button data-tab="pricing">Pricing</button>
</div>
<div data-panel="overview">Overview…</div>
<div data-panel="pricing">Pricing…</div>
</dom-tabs>Props
Props
| Name | Type | Default | Description |
|---|---|---|---|
| v-model | string | first tab key | Active tab key. |
| tabs | Array<{ key, label }> | — | Tabs to render. |
| variant | 'pill' | 'flat' | 'page' | 'pill' | Use flat for underlined navigation or page for editor-style tabs attached to a panel. |
| fill | boolean | false | Stretch the tab list and active panel inside a flex parent. |
Keyboard
- ← / →Move to previous / next tab.
- Home / EndJump to first / last tab.
- Enter / SpaceActivate focused tab.