---
title: "Tabs"
description: "A set of layered sections of content—known as tab panels—that are displayed one at a time."
canonical_url: "https://vinicunca.dev/akar/components/tabs"
---
# Tabs

> A set of layered sections of content—known as tab panels—that are displayed one at a time.

```vue [index.vue]
<script setup lang="ts">
import { TabsContent, TabsIndicator, TabsList, TabsRoot, TabsTrigger } from 'akar'
import './styles.css'
</script>

<template>
  <div class="akar-demo akar-demo-Tabs">
    <TabsRoot
        class="TabsRoot"
        default-value="tab1"
      >
        <TabsList
          class="TabsList"
          aria-label="Manage your account"
        >
          <TabsIndicator class="TabsIndicator">
            <div class="TabsIndicatorContent"/>
          </TabsIndicator>
          <TabsTrigger
            class="TabsTrigger"
            value="tab1"
          >
            Account
          </TabsTrigger>
          <TabsTrigger
            class="TabsTrigger"
            value="tab2"
          >
            Password
          </TabsTrigger>
        </TabsList>
        <TabsContent
          class="TabsContent"
          value="tab1"
        >
          <p class="Text">
            Make changes to your account here. Click save when you're done.
          </p>
          <fieldset class="Fieldset">
            <label
              class="Label"
              for="name"
            > Name </label>
            <input
              id="name"
              class="Input"
              value="Pedro Duarte"
            >
          </fieldset>
          <fieldset class="Fieldset">
            <label
              class="Label"
              for="username"
            > Username </label>
            <input
              id="username"
              class="Input"
              value="@peduarte"
            >
          </fieldset>
          <div :style="{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }">
            <button
              class="Button green"
            >
              Save changes
            </button>
          </div>
        </TabsContent>
        <TabsContent
          class="TabsContent"
          value="tab2"
        >
          <p class="Text">
            Change your password here. After saving, you'll be logged out.
          </p>
          <fieldset class="Fieldset">
            <label
              class="Label"
              for="currentPassword"
            >
              Current password
            </label>
            <input
              id="currentPassword"
              class="Input"
              type="password"
            >
          </fieldset>
          <fieldset class="Fieldset">
            <label
              class="Label"
              for="newPassword"
            > New password </label>
            <input
              id="newPassword"
              class="Input"
              type="password"
            >
          </fieldset>
          <fieldset class="Fieldset">
            <label
              class="Label"
              for="confirmPassword"
            >
              Confirm password
            </label>
            <input
              id="confirmPassword"
              class="Input"
              type="password"
            >
          </fieldset>
          <div :style="{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }">
            <button
              class="Button green"
            >
              Change password
            </button>
          </div>
        </TabsContent>
      </TabsRoot>
  </div>
</template>
```

```css [styles.css]
@import '../_colors/black-alpha.css';
@import '../_colors/green.css';
@import '../_colors/mauve.css';
@import '../_colors/grass.css';

@scope (.akar-demo-Tabs) {
/* reset */
button,
fieldset,
input {
  all: unset;
}

.TabsRoot {
  display: flex;
  flex-direction: column;
  width: 300px;
  box-shadow: 0 2px 10px var(--black-a4);
}

.TabsList {
  flex-shrink: 0;
  display: flex;
  border-bottom: 1px solid var(--mauve-6);
  position: relative;
}

.TabsIndicator {
  position: absolute;
  padding-left: 2rem;
  left: 0;
  height: 2px;
  bottom: 0;
  width: var(--akar-tabs-indicator-size);
  transform: translateX(var(--akar-tabs-indicator-position));
  border-radius: 9999px; /* rounded-full equivalent */
  transition-property: width, transform;
  transition-duration: 300ms;
}

.TabsIndicatorContent {
  background-color: var(--grass-8);
  height: 100%;
  width: 100%;
}

.TabsTrigger {
  font-family: inherit;
  background-color: white;
  padding: 0 20px;
  height: 45px;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  color: var(--mauve-11);
  user-select: none;
}
.TabsTrigger:first-child {
  border-top-left-radius: 6px;
}
.TabsTrigger:last-child {
  border-top-right-radius: 6px;
}
.TabsTrigger:hover {
  color: var(--grass-11);
}
.TabsTrigger[data-state='active'] {
  color: var(--grass-11);
  box-shadow: inset 0 -1px 0 0 currentColor, 0 1px 0 0 currentColor;
}
.TabsTrigger:focus {
  position: relative;
  box-shadow: 0 0 0 2px black;
}

.TabsContent {
  flex-grow: 1;
  padding: 20px;
  background-color: white;
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
  outline: none;
}
.TabsContent:focus {
  box-shadow: 0 0 0 2px black;
}

.Text {
  margin-top: 0;
  margin-bottom: 20px;
  color: var(--mauve-11);
  font-size: 15px;
  line-height: 1.5;
}

.Fieldset {
  margin-bottom: 15px;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.Label {
  font-size: 13px;
  line-height: 1;
  margin-bottom: 10px;
  color: var(--grass-12);
  display: block;
}

.Input {
  flex: 1 0 auto;
  border-radius: 4px;
  padding: 0 10px;
  font-size: 15px;
  line-height: 1;
  color: var(--grass-11);
  box-shadow: 0 0 0 1px var(--grass-7);
  height: 35px;
}
.Input:focus {
  box-shadow: 0 0 0 2px var(--grass-8);
}

.Button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  padding: 0 15px;
  font-size: 15px;
  line-height: 1;
  font-weight: 500;
  height: 35px;
}
.Button.green {
  background-color: var(--green-4);
  color: var(--green-11);
}
.Button.green:hover {
  background-color: var(--green-5);
}
.Button.green:focus {
  box-shadow: 0 0 0 2px var(--green-7);
}
}
```

## Features

- Can be controlled or uncontrolled.
- Supports horizontal/vertical orientation.
- Supports automatic/manual activation.
- Full keyboard navigation.

## Installation

Install the component from your command line.

```bash [pnpm]
pnpm add akar
```

```bash [yarn]
yarn add akar
```

```bash [npm]
npm install akar
```

```bash [bun]
bun add akar
```

## Anatomy

Import all parts and piece them together.

```vue
<script setup>
import { TabsContent, TabsIndicator, TabsList, TabsRoot, TabsTrigger } from 'akar';
</script>

<template>
  <TabsRoot>
    <TabsList>
      <TabsIndicator />
      <TabsTrigger />
    </TabsList>
    <TabsContent />
  </TabsRoot>
</template>
```

## API Reference

### Root

Contains all the tabs component parts.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `activationMode` | `"automatic"` | `"automatic" \| "manual"` | Whether a tab is activated automatically (on focus) or manually (on click). |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `defaultValue` |  | `T` | The value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs |
| `dir` |  | `"ltr" \| "rtl"` | The reading direction of the combobox when applicable.<br><br>If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
| `modelValue` |  | `T` | The controlled value of the tab to activate. Can be bind as `v-model`. |
| `orientation` | `"horizontal"` | `"vertical" \| "horizontal"` | The orientation the tabs are laid out.<br>Mainly so arrow navigation is done accordingly (left & right vs. up & down) |
| `unmountOnHide` | `true` | `boolean` | When `true`, the element will be unmounted on closed state. |

**Events**

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `payload: T` | Event handler called when the value changes |

**Slots**

| Slot | Payload | Description |
| --- | --- | --- |
| `modelValue` | `T` | Current input values |

[TabsRoot.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Tabs/TabsRoot.vue)

| Data attribute | Value |
| --- | --- |
| `[data-orientation]` | "vertical" \\| "horizontal" |

### List

Contains the triggers that are aligned along the edge of the active content.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `loop` | `true` | `boolean` | When `true`, keyboard navigation will loop from last tab to first, and vice versa. |

[TabsList.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Tabs/TabsList.vue)

| Data attribute | Value |
| --- | --- |
| `[data-orientation]` | "vertical" \\| "horizontal" |

### Trigger

The button that activates its associated content.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"button"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `disabled` | `false` | `boolean` | When `true`, prevents the user from interacting with the tab. |
| `value*` |  | `string \| number` | A unique value that associates the trigger with a content. |

[TabsTrigger.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Tabs/TabsTrigger.vue)

| Data attribute | Value |
| --- | --- |
| `[data-state]` | "active" \\| "inactive" |
| `[data-disabled]` | Present when disabled |
| `[data-orientation]` | "vertical" \\| "horizontal" |

### Indicator

The indicator that highlights the current active tab.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |

**Methods**

| Method | Type | Description |
| --- | --- | --- |
| `updateIndicatorStyle` | `() => void` |  |

[TabsIndicator.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Tabs/TabsIndicator.vue)

| CSS variable | Description |
| --- | --- |
| `--akar-tabs-indicator-size` | The size of the indicator. |
| `--akar-tabs-indicator-thickness` | The thickness of the indicator. |
| `--akar-tabs-indicator-position` | The position of the indicator |

### Content

Contains the content associated with each trigger.

> [!NOTE]
> 
> Defer mounting with `forceMount` / [`Presence`](https://vinicunca.dev/akar/utilities/presence). See the [animation guide](https://vinicunca.dev/akar/guides/animation).

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `forceMount` |  | `boolean` | Used to force mounting when more control is needed. Useful when<br>controlling animation with Vue animation libraries. |
| `value*` |  | `string \| number` | A unique value that associates the content with a trigger. |

[TabsContent.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Tabs/TabsContent.vue)

| Data attribute | Value |
| --- | --- |
| `[data-state]` | "active" \\| "inactive" |
| `[data-orientation]` | "vertical" \\| "horizontal" |

## Examples

### Vertical

You can create vertical tabs by using the `orientation` prop.

```vue
<script setup>
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'akar'
</script>

<template>
  <TabsRoot
    default-value="tab1"
    orientation="vertical"
  >
    <TabsList aria-label="tabs example">
      <TabsTrigger value="tab1">
        One
      </TabsTrigger>
      <TabsTrigger value="tab2">
        Two
      </TabsTrigger>
      <TabsTrigger value="tab3">
        Three
      </TabsTrigger>
    </TabsList>
    <TabsContent value="tab1">
      Tab one content
    </TabsContent>
    <TabsContent value="tab2">
      Tab two content
    </TabsContent>
    <TabsContent value="tab3">
      Tab three content
    </TabsContent>
  </TabsRoot>
</template>
```

## Accessibility

Adheres to the [Tabs WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel).

### Keyboard Interactions

| Key | Description |
| --- | --- |
| <kbd>Tab</kbd> |  When focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content. |
| <kbd>ArrowDown</kbd> |  Moves focus to the next trigger depending on `orientation` and activates its associated content. |
| <kbd>ArrowRight</kbd> |  Moves focus to the next trigger depending on `orientation` and activates its associated content. |
| <kbd>ArrowUp</kbd> |  Moves focus to the previous trigger depending on `orientation` and activates its associated content. |
| <kbd>ArrowLeft</kbd> |  Moves focus to the previous trigger depending on `orientation` and activates its associated content. |
| <kbd>Home</kbd> |  Moves focus to the first trigger and activates its associated content. |
| <kbd>End</kbd> |  Moves focus to the last trigger and activates its associated content. |

---

- [ARIA](https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel)
- [GitHub](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Tabs)
- [npm](https://www.npmjs.com/package/akar)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
