# Tabs

Horizontal or vertical tabbed interface with List, Trigger, and Content subparts. Supports three visual variants and a bindable active value.

- Package: `@mielui/svelte`
- Component version: `1.1.1`
- Depends on Mielui components: _internal/utils
- Shared utilities: utils.cn, utils.createContext, utils.pressable

## Install

~~~~sh
pnpm dlx @mielui/svelte add tabs
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Content

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| value | string | - | Yes | No | Identifies the panel. It matches a trigger's value. |
| forceMount | boolean \| undefined | false | No | No | Keeps the panel mounted while hidden, so its state survives. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### List

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| id | string \| undefined | - | No | No | Id of the tab list element. |
| aria-label | string \| undefined | - | No | No | Accessible name for the tab list. |
| aria-labelledby | string \| undefined | - | No | No | Id of the element that names the tab list. |



### Root

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| activationMode | "automatic" \| "manual" \| undefined | 'automatic' | No | No | `automatic` selects a tab on focus. `manual` waits for Enter or Space. |
| value | string \| undefined | '' | No | Yes | Selected tab. Bindable. |
| onValueChange | ((value: string) => void) \| undefined | - | No | No | Called with the new value when it changes. |
| orientation | "horizontal" \| "vertical" \| undefined | 'horizontal' | No | No | Direction of the tab list. It also sets which arrow keys move between tabs. |
| variant | TabsVariant \| undefined | 'default' | No | No | Style of the tab list. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Trigger

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| value | string | - | Yes | No | Identifies the tab. It matches a panel's value. |
| disabled | boolean \| undefined | false | No | No | Prevents interaction and dims the control. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Public types

~~~~ts
import type { DefaultProps } from '@mielui/svelte/utils';
import Root from './tabs.svelte';
import Content from './tabs-content.svelte';
import List from './tabs-list.svelte';
import Trigger from './tabs-trigger.svelte';

export type TabsVariant = 'default' | 'ghost' | 'segmented';

export type TabsProps = {
    /** `automatic` selects a tab on focus. `manual` waits for Enter or Space. */
    activationMode?: 'automatic' | 'manual';
    /** Selected tab. Bindable. */
    value?: string;
    /** Called with the new value when it changes. */
    onValueChange?: (value: string) => void;
    /** Direction of the tab list. It also sets which arrow keys move between tabs. */
    orientation?: 'horizontal' | 'vertical';
    /** Style of the tab list. */
    variant?: TabsVariant;
} & DefaultProps;

export type TabsListProps = DefaultProps & {
    /** Id of the tab list element. */
    id?: string;
    /** Accessible name for the tab list. */
    'aria-label'?: string;
    /** Id of the element that names the tab list. */
    'aria-labelledby'?: string;
};

export type TabsTriggerProps = {
    /** Identifies the tab. It matches a panel's value. */
    value: string;
    /** Prevents interaction and dims the control. */
    disabled?: boolean;
} & DefaultProps;

export type TabsContentProps = {
    /** Identifies the panel. It matches a trigger's value. */
    value: string;
    /** Keeps the panel mounted while hidden, so its state survives. */
    forceMount?: boolean;
} & DefaultProps;

export { Content, List, Root, Trigger };
~~~~

## Examples

### Manual

~~~~svelte
<script lang="ts">
    import { Input } from '@mielui/svelte/components/input';
    import * as Tabs from '@mielui/svelte/components/tabs';

    let tab = $state('draft');
</script>

<div class="w-full max-w-sm">
    <Tabs.Root bind:value={tab} activationMode="manual">
        <Tabs.List aria-label="Draft">
            <Tabs.Trigger value="draft">Draft</Tabs.Trigger>
            <Tabs.Trigger value="preview">Preview</Tabs.Trigger>
            <Tabs.Trigger value="history" disabled>History</Tabs.Trigger>
        </Tabs.List>
        <Tabs.Content value="draft" forceMount class="pt-4">
            <Input label="Draft title" placeholder="Type here, then switch tabs" />
        </Tabs.Content>
        <Tabs.Content value="preview" class="pt-4">
            <p class="text-sm text-foreground-muted">
                Return to Draft to see the input still holds your text.
            </p>
        </Tabs.Content>
    </Tabs.Root>
</div>
~~~~

### Panels

~~~~svelte
<script lang="ts">
    import * as Tabs from '@mielui/svelte/components/tabs';

    let tab = $state('overview');
</script>

<div class="w-full max-w-sm">
    <Tabs.Root bind:value={tab}>
        <Tabs.List class="grid w-full grid-cols-3">
            <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
            <Tabs.Trigger value="activity">Activity</Tabs.Trigger>
            <Tabs.Trigger value="files">Files</Tabs.Trigger>
        </Tabs.List>
        <Tabs.Content value="overview" class="pt-4">
            <p class="text-sm font-medium text-foreground">Project overview</p>
            <p class="mt-1 text-sm text-foreground-muted">Everything is up to date.</p>
        </Tabs.Content>
        <Tabs.Content value="activity" class="pt-4">
            <p class="text-sm font-medium text-foreground">Recent activity</p>
            <p class="mt-1 text-sm text-foreground-muted">You updated the project settings.</p>
        </Tabs.Content>
        <Tabs.Content value="files" class="pt-4">
            <p class="text-sm font-medium text-foreground">Files</p>
            <p class="mt-1 text-sm text-foreground-muted">README.md and package.json</p>
        </Tabs.Content>
    </Tabs.Root>
</div>
~~~~

### Variant Default

~~~~svelte
<script lang="ts">
    import * as Tabs from '@mielui/svelte/components/tabs';

    let tab = $state('overview');
</script>

<Tabs.Root bind:value={tab}>
    <Tabs.List>
        <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
        <Tabs.Trigger value="activity">Activity</Tabs.Trigger>
        <Tabs.Trigger value="files">Files</Tabs.Trigger>
    </Tabs.List>
</Tabs.Root>
~~~~

### Variant Ghost

~~~~svelte
<script lang="ts">
    import * as Tabs from '@mielui/svelte/components/tabs';

    let tab = $state('overview');
</script>

<Tabs.Root bind:value={tab} variant="ghost">
    <Tabs.List>
        <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
        <Tabs.Trigger value="activity">Activity</Tabs.Trigger>
        <Tabs.Trigger value="files">Files</Tabs.Trigger>
    </Tabs.List>
</Tabs.Root>
~~~~

### Variant Segmented

~~~~svelte
<script lang="ts">
    import * as Tabs from '@mielui/svelte/components/tabs';

    let tab = $state('vanilla');
</script>

<Tabs.Root bind:value={tab} variant="segmented">
    <Tabs.List>
        <Tabs.Trigger value="vanilla">Vanilla</Tabs.Trigger>
        <Tabs.Trigger value="react">React</Tabs.Trigger>
        <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
        <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
        <Tabs.Trigger value="svelte">Svelte</Tabs.Trigger>
    </Tabs.List>
</Tabs.Root>
~~~~

### Vertical

~~~~svelte
<script lang="ts">
    import * as Tabs from '@mielui/svelte/components/tabs';

    let tab = $state('account');
</script>

<div class="w-full max-w-lg">
    <Tabs.Root bind:value={tab} orientation="vertical" variant="ghost">
        <Tabs.List class="min-w-28">
            <Tabs.Trigger value="account">Account</Tabs.Trigger>
            <Tabs.Trigger value="notifications">Notifications</Tabs.Trigger>
            <Tabs.Trigger value="appearance">Appearance</Tabs.Trigger>
        </Tabs.List>
        <Tabs.Content value="account" class="py-1.5">
            <p class="text-sm font-medium text-foreground">Account</p>
            <p class="mt-1 text-sm text-foreground-muted">
                Manage your profile and contact details.
            </p>
        </Tabs.Content>
        <Tabs.Content value="notifications" class="py-1.5">
            <p class="text-sm font-medium text-foreground">Notifications</p>
            <p class="mt-1 text-sm text-foreground-muted">
                Choose which updates you want to receive.
            </p>
        </Tabs.Content>
        <Tabs.Content value="appearance" class="py-1.5">
            <p class="text-sm font-medium text-foreground">Appearance</p>
            <p class="mt-1 text-sm text-foreground-muted">
                Adjust the theme and interface density.
            </p>
        </Tabs.Content>
    </Tabs.Root>
</div>
~~~~

For the rendered reference, visit [/docs/components/tabs](/docs/components/tabs).
