---
title: Tabs
description: Tabbed navigation component with configurable orientation and variant styles.
sidebar:
  order: 2
search:
  tags: [tablist, panels, navigation, selection]
---

# Tabs

> Tabbed navigation component with configurable orientation and variant styles.

## Import

```tsx
import { Tabs } from 'moraine'
```

## Slot Structure

Tab list with triggers and associated content panels.

```text
root
├── list
│   ├── indicator (optional)
│   └── trigger (×n)
│       ├── leading (Icon, optional)
│       └── label (optional)
└── content (×n)
```

## Examples

### Variants

Pill and link visual variants for the tab list.

```tsx
function Variants() {
  return (
    <div class="flex flex-col gap-8 w-xl">
      <Tabs
        defaultValue="overview"
        variant="pill"
        items={[
          {
            label: 'Overview',
            value: 'overview',
            icon: 'i-lucide:layout-dashboard',
            content: <p class="text-sm text-foreground">Overview panel content.</p>,
          },
          {
            label: 'Settings',
            value: 'settings',
            icon: 'i-lucide:settings',
            content: <p class="text-sm text-foreground">Settings panel content.</p>,
          },
          {
            label: 'Billing',
            value: 'billing',
            icon: 'i-lucide:credit-card',
            content: <p class="text-sm text-foreground">Billing panel content.</p>,
          },
        ]}
      />
      <Tabs
        defaultValue="settings"
        variant="link"
        items={[
          {
            label: 'Overview',
            value: 'overview',
            icon: 'i-lucide:layout-dashboard',
            content: <p class="text-sm text-foreground">Overview panel content.</p>,
          },
          {
            label: 'Settings',
            value: 'settings',
            icon: 'i-lucide:settings',
            content: <p class="text-sm text-foreground">Settings panel content.</p>,
          },
          {
            label: 'Billing',
            value: 'billing',
            icon: 'i-lucide:credit-card',
            content: <p class="text-sm text-foreground">Billing panel content.</p>,
          },
        ]}
      />
    </div>
  )
}
```

### Sizes

Trigger size scale from xs to xl.

```tsx
function Sizes() {
  const SIZE_OPTIONS = ['xs', 'sm', 'md', 'lg', 'xl'] as const

  return (
    <div class="w-xl space-y-4">
      <For each={SIZE_OPTIONS}>
        {(size) => (
          <div class="space-y-1">
            <p class="text-xs text-muted-foreground tracking-wide uppercase">{size}</p>
            <Tabs
              size={size}
              defaultValue="overview"
              items={[
                {
                  label: 'Overview',
                  value: 'overview',
                  content: <p class="text-sm text-foreground">Overview</p>,
                },
                {
                  label: 'Metrics',
                  value: 'metrics',
                  content: <p class="text-sm text-foreground">Metrics</p>,
                },
                {
                  label: 'Activity',
                  value: 'activity',
                  content: <p class="text-sm text-foreground">Activity</p>,
                },
              ]}
            />
          </div>
        )}
      </For>
    </div>
  )
}
```

### Orientations

Horizontal and vertical orientation with the same items contract.

```tsx
function Orientations() {
  return (
    <div class="flex gap-8 w-2xl">
      <Tabs
        defaultValue="overview"
        orientation="horizontal"
        items={[
          {
            label: 'Overview',
            value: 'overview',
            icon: 'i-lucide:layout-dashboard',
            content: <p class="text-sm text-foreground">Overview panel content.</p>,
          },
          {
            label: 'Settings',
            value: 'settings',
            icon: 'i-lucide:settings',
            content: <p class="text-sm text-foreground">Settings panel content.</p>,
          },
          {
            label: 'Billing',
            value: 'billing',
            icon: 'i-lucide:credit-card',
            content: <p class="text-sm text-foreground">Billing panel content.</p>,
          },
        ]}
      />
      <Tabs
        defaultValue="settings"
        orientation="vertical"
        classes={{
          root: 'max-w-md',
          list: 'w-40',
        }}
        items={[
          {
            label: 'Overview',
            value: 'overview',
            icon: 'i-lucide:layout-dashboard',
            content: <p class="text-sm text-foreground w-sm">Overview panel content.</p>,
          },
          {
            label: 'Settings',
            value: 'settings',
            icon: 'i-lucide:settings',
            content: <p class="text-sm text-foreground w-sm">Settings panel content.</p>,
          },
          {
            label: 'Billing',
            value: 'billing',
            icon: 'i-lucide:credit-card',
            content: <p class="text-sm text-foreground w-sm">Billing panel content.</p>,
          },
        ]}
      />
    </div>
  )
}
```

### Controlled + Disabled Items

Controlled value with disabled tab options and external navigation controls.

```tsx
function ControlledDisabledItems() {
  const [value, setValue] = createSignal('overview')

  return (
    <div class="space-y-3">
      <div class="flex flex-wrap gap-2">
        <Button size="sm" variant="outline" onClick={() => setValue('overview')}>
          Go overview
        </Button>
        <Button size="sm" variant="outline" onClick={() => setValue('settings')}>
          Go settings
        </Button>
        <Button size="sm" variant="secondary" onClick={() => setValue('billing')}>
          Try disabled billing
        </Button>
      </div>

      <Tabs
        value={value()}
        onChange={setValue}
        variant="link"
        items={[
          {
            label: 'Overview',
            value: 'overview',
            content: <p class="text-sm text-foreground">Overview section with release status.</p>,
          },
          {
            label: 'Settings',
            value: 'settings',
            content: (
              <p class="text-sm text-foreground">Settings section with environment options.</p>
            ),
          },
          {
            label: 'Billing (Disabled)',
            value: 'billing',
            disabled: true,
            content: <p class="text-sm text-foreground">This panel is intentionally disabled.</p>,
          },
        ]}
      />
      <p class="text-xs text-muted-foreground">Current tab value: {value()}</p>
    </div>
  )
}
```

## API Reference

### Attributes

#### `root`

Tabs container that owns tab selection and panel rendering.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-orientation | string \| undefined | Stores the rendered orientation. |

#### `list`

Tablist that contains all tab triggers and the selection indicator.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-orientation | boolean \| string \| undefined | Communicates horizontal or vertical orientation. |
| role | string | Defines the semantic role exposed to assistive technology. |

#### `indicator`

Moving indicator aligned with the active tab trigger.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-hidden | boolean \| string \| undefined | Hides decorative content from assistive technology. |

#### `trigger`

Tab button users activate to select a panel.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-highlighted | string \| undefined | Present when the item is highlighted by pointer or keyboard navigation. |
| data-selected | string \| undefined | Present when the item is selected. |

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-controls | boolean \| string \| undefined | References the controlled element while the related content is mounted. |
| aria-selected | boolean \| string \| undefined | Indicates the currently selected option or tab. |
| role | string | Defines the semantic role exposed to assistive technology. |

#### `leading`

Optional icon rendered before a tab label.

#### `label`

Text or custom label rendered inside a tab trigger.

#### `trailing`

Optional trailing content rendered after a tab label.

#### `content`

Tab panel rendered for the selected item.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-selected | string \| undefined | Present when the item is selected. |

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-labelledby | boolean \| string \| undefined | References the element that labels the control or region. |
| role | string | Defines the semantic role exposed to assistive technology. |

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| activationMode | "manual" \| "automatic" \| undefined | automatic | Whether keyboard navigation activates the tab immediately or waits for confirmation. |
| class | ClassValue | — | Class applied to the component root or trigger element. |
| classes | TabsT.Classes \| undefined | — | — |
| defaultValue | string \| undefined | — | Default active tab value for uncontrolled usage. |
| disabled | boolean \| undefined | false | Whether the tab list is disabled. |
| id | string \| undefined | — | Unique identifier for the tabs root element. |
| items | TabsT.Item[] \| undefined | — | Array of tabs to display. |
| keyboardLoop | boolean \| undefined | true | Whether arrow-key navigation wraps from the ends. |
| onChange | ((value: string) => void) \| undefined | — | Callback when the active tab changes. |
| orientation | "horizontal" \| "vertical" \| undefined | horizontal | The orientation of the tab list. |
| ref | JSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never \| undefined | — | — |
| size | "xs" \| "sm" \| "md" \| "lg" \| "xl" \| undefined | — | — |
| style | JSX.CSSProperties \| undefined | — | — |
| styles | TabsT.Styles \| undefined | — | — |
| value | string \| undefined | — | Controlled active tab value. |
| variant | "link" \| "pill" \| undefined | — | — |

### Items

An individual tab in the tabs component.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| content | JSX.Element | — | Content to display when the tab is active. |
| disabled | boolean \| undefined | false | Whether the tab is disabled. |
| icon | IconT.Name | — | Icon to display next to the label. |
| label | JSX.Element | — | Label to display on the tab trigger. |
| value | string \| undefined | index of the item | Unique value for the tab. |

### ARIA

Accessibility attributes and roles emitted by the component markup.

| Attribute | Type | Description |
| --- | --- | --- |
| aria-controls | boolean \| string \| undefined | References the controlled element while the related content is mounted. |
| aria-hidden | boolean \| string \| undefined | Hides decorative content from assistive technology. |
| aria-labelledby | boolean \| string \| undefined | References the element that labels the control or region. |
| aria-orientation | boolean \| string \| undefined | Communicates horizontal or vertical orientation. |
| aria-selected | boolean \| string \| undefined | Indicates the currently selected option or tab. |
| role | string | Defines the semantic role exposed to assistive technology. |

### Data Attributes

State and slot attributes exposed for styling hooks and selectors.

| Attribute | Type | Description |
| --- | --- | --- |
| data-highlighted | string \| undefined | Present when the item is highlighted by pointer or keyboard navigation. |
| data-orientation | string \| undefined | Stores the rendered orientation. |
| data-selected | string \| undefined | Present when the item is selected. |
| data-slot | string | Identifies the rendered slot for styling hooks and selectors. |
