# Tabs

URL: https://ark-ui.com/docs/components/tabs
LLM: https://ark-ui.com/llms.txt/components/tabs

Flexible navigation tool with various modes and features.

---



## Anatomy



```tsx
<Tabs.Root>
  <Tabs.List>
    <Tabs.Trigger />
    <Tabs.Indicator />
  </Tabs.List>
  <Tabs.Content />
</Tabs.Root>
```

## Examples

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const Basic = () => (
  <Tabs.Root className={styles.Root} defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.Trigger} value="account">
        Account
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="password">
        Password
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="billing">
        Billing
      </Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

### Controlled

To create a controlled Tabs component, manage the current selected tab using the `value` prop and update it when the
`onValueChange` event handler is called:

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import { useState } from 'react'
import styles from 'styles/tabs.module.css'

export const Controlled = () => {
  const [value, setValue] = useState<string | null>('account')
  return (
    <Tabs.Root className={styles.Root} value={value} onValueChange={(e) => setValue(e.value)}>
      <Tabs.List className={styles.List}>
        <Tabs.Trigger className={styles.Trigger} value="account">
          Account
        </Tabs.Trigger>
        <Tabs.Trigger className={styles.Trigger} value="password">
          Password
        </Tabs.Trigger>
        <Tabs.Trigger className={styles.Trigger} value="billing">
          Billing
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content className={styles.Content} value="account">
        Make changes to your account here.
      </Tabs.Content>
      <Tabs.Content className={styles.Content} value="password">
        Change your password here.
      </Tabs.Content>
      <Tabs.Content className={styles.Content} value="billing">
        Manage your billing and payment details.
      </Tabs.Content>
    </Tabs.Root>
  )
}
```

### Root Provider

An alternative way to control the tabs is to use the `RootProvider` component and the `useTabs` hook. This way you can
access the state and methods from outside the component.

```tsx
import { Tabs, useTabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const RootProvider = () => {
  const tabs = useTabs({ defaultValue: 'account' })

  return (
    <div className="stack">
      <output>selected: {tabs.value}</output>
      <Tabs.RootProvider className={styles.Root} value={tabs}>
        <Tabs.List className={styles.List}>
          <Tabs.Trigger className={styles.Trigger} value="account">
            Account
          </Tabs.Trigger>
          <Tabs.Trigger className={styles.Trigger} value="password">
            Password
          </Tabs.Trigger>
          <Tabs.Trigger className={styles.Trigger} value="billing">
            Billing
          </Tabs.Trigger>
        </Tabs.List>
        <Tabs.Content className={styles.Content} value="account">
          Make changes to your account here.
        </Tabs.Content>
        <Tabs.Content className={styles.Content} value="password">
          Change your password here.
        </Tabs.Content>
        <Tabs.Content className={styles.Content} value="billing">
          Manage your billing and payment details.
        </Tabs.Content>
      </Tabs.RootProvider>
    </div>
  )
}
```

### Indicator

To provide a visual cue for the selected tab, use the `Tabs.Indicator` component:

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const Indicator = () => (
  <Tabs.Root className={styles.Root} defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.TriggerIndicator} value="account">
        Account
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.TriggerIndicator} value="password">
        Password
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.TriggerIndicator} value="billing">
        Billing
      </Tabs.Trigger>
      <Tabs.Indicator className={styles.Indicator} />
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

### Disabled

To disable a tab, simply pass the `disabled` prop to the `Tabs.Trigger` component:

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const DisabledTab = () => (
  <Tabs.Root className={styles.Root} defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.Trigger} value="account">
        Account
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="password" disabled>
        Password
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="billing">
        Billing
      </Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

### Vertical

The default orientation of the tabs is `horizontal`. To change the orientation, set the `orientation` prop to
`vertical`.

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const Vertical = () => (
  <Tabs.Root className={styles.Root} orientation="vertical" defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.Trigger} value="account">
        Account
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="password">
        Password
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="billing">
        Billing
      </Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

### Lazy Mount

Lazy mounting is a feature that allows the content of a tab to be rendered only when the tab is first activated. This is
useful for performance optimization, especially when tab content is large or complex. To enable lazy mounting, use the
`lazyMount` prop on the `Tabs.Content` component.

In addition, the `unmountOnExit` prop can be used in conjunction with `lazyMount` to unmount the tab content when the
tab is deactivated, freeing up resources. The next time the tab is activated, its content will be re-rendered.

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const LazyMount = () => (
  <Tabs.Root className={styles.Root} lazyMount unmountOnExit defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.TriggerIndicator} value="account">
        Account
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.TriggerIndicator} value="password">
        Password
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.TriggerIndicator} value="billing">
        Billing
      </Tabs.Trigger>
      <Tabs.Indicator className={styles.Indicator} />
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

### Manual Activation

By default, the tab can be selected when it receives focus from either the keyboard or pointer interaction. This is
called automatic tab activation.

In contrast, manual tab activation means the tab is selected with the

<kbd>Enter</kbd> key or by clicking on the tab.

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const ManualActivation = () => (
  <Tabs.Root className={styles.Root} activationMode="manual" defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.Trigger} value="account">
        Account
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="password">
        Password
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="billing">
        Billing
      </Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

### Links

Use the `asChild` prop to render tab triggers as anchor links. This is useful for SEO and allows tabs to work with
browser navigation.

```tsx
import { Tabs } from '@ark-ui/react/tabs'
import styles from 'styles/tabs.module.css'

export const Links = () => (
  <Tabs.Root className={styles.Root} defaultValue="account">
    <Tabs.List className={styles.List}>
      <Tabs.Trigger className={styles.Trigger} value="account" asChild>
        <a href="#account">Account</a>
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="password" asChild>
        <a href="#password">Password</a>
      </Tabs.Trigger>
      <Tabs.Trigger className={styles.Trigger} value="billing" asChild>
        <a href="#billing">Billing</a>
      </Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content className={styles.Content} value="account">
      Make changes to your account here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="password">
      Change your password here.
    </Tabs.Content>
    <Tabs.Content className={styles.Content} value="billing">
      Manage your billing and payment details.
    </Tabs.Content>
  </Tabs.Root>
)
```

## Guides

### Router Integration

When using frameworks like Next.js, Remix, or React Router, controlling the active tabs based on the URL can be useful.

To achieve this, you need to do two things:

- Set the `value` prop to the current URL path.
- Listen to the `onValueChange` event and update the URL path.

Here's an example using Remix Router

```tsx
import { Tabs } from '@ark-ui/<framework>/tabs'
import { useLocation, useNavigate, Link } from '@remix-run/react'

export default function App() {
  const { pathname } = useLocation()
  const navigate = useNavigate()
  const lastPathFragment = pathname.substring(pathname.lastIndexOf('/') + 1)
  const activeTab = lastPathFragment.length > 0 ? lastPathFragment : 'homepage'

  return (
    <Tabs.Root
      value={activeTab}
      onValueChange={({ value }) => {
        navigate(`/${value === 'home' ? '' : value}`)
      }}
    >
      <Tabs.List>
        <Tabs.Trigger asChild value="home">
          <Link to="">Home</Link>
        </Tabs.Trigger>
        <Tabs.Trigger asChild value="page-1">
          <Link to="page-1">Page 1</Link>
        </Tabs.Trigger>
        <Tabs.Trigger asChild value="page-2">
          <Link to="page-2">Page 2</Link>
        </Tabs.Trigger>
      </Tabs.List>
    </Tabs.Root>
  )
}
```

## API Reference

### Props

### Root

#### Props

**`activationMode`**
Type: `'manual' | 'automatic'`
Required: false
Default Value: `"automatic"`
Description: The activation mode of the tabs. Can be `manual` or `automatic`
- `manual`: Tabs are activated when clicked or press `enter` key.
- `automatic`: Tabs are activated when receiving focus

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`composite`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tab is composite

**`defaultValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial selected tab value when rendered.
Use when you don't need to control the selected tab value.

**`deselectable`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the active tab can be deselected when clicking on it.

**`hideMode`**
Type: `HideMode`
Required: false
Default Value: `'display-none'`
Description: How to hide content when mounted but not present.
- `'display-none'`: HTML `hidden` attribute. Effects stay alive.
- `'activity'`: React 19 `<Activity mode="hidden">`. Effects pause. Requires React 19+.

**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.

**`ids`**
Type: `Partial<{
  root: string
  trigger: (value: string) => string
  list: string
  content: (value: string) => string
  indicator: string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the tabs. Useful for composition.

**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting

**`loopFocus`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the keyboard navigation will loop from last tab to first, and vice versa.

**`navigate`**
Type: `(details: NavigateDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to navigate to the selected tab when clicking on it.
Useful if tab triggers are anchor elements.

**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback to be called when the focused tab changes

**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Callback to be called when the selected/active tab changes

**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the tabs. Can be `horizontal` or `vertical`
- `horizontal`: only left and right arrow key navigation will work.
- `vertical`: only up and down arrow key navigation will work.

**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states

**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.

**`value`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled selected tab value

#### Data Attributes

**`data-scope`**: tabs
**`data-part`**: root
**`data-orientation`**: The orientation of the tabs
**`data-focus`**: Present when focused

### TabContent

#### Props

**`value`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The value of the tab

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### TabIndicator

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### TabList

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### TabTrigger

#### Props

**`value`**
Type: `string`
Required: true
Default Value: `undefined`
Description: The value of the tab

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the tab is disabled

### RootProvider

#### Props

**`value`**
Type: `UseTabsReturn`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`hideMode`**
Type: `HideMode`
Required: false
Default Value: `'display-none'`
Description: How to hide content when mounted but not present.
- `'display-none'`: HTML `hidden` attribute. Effects stay alive.
- `'activity'`: React 19 `<Activity mode="hidden">`. Effects pause. Requires React 19+.

**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting

**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.

### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `value` | `string | null` | The current value of the tabs. |
| `focusedValue` | `string | null` | The value of the tab that is currently focused. |
| `setValue` | `(value: string) => void` | Sets the value of the tabs. |
| `clearValue` | `VoidFunction` | Clears the value of the tabs. |
| `setIndicatorRect` | `(value: string) => void` | Sets the indicator rect to the tab with the given value |
| `syncTabIndex` | `VoidFunction` | Synchronizes the tab index of the content element.
Useful when rendering tabs within a select or combobox |
| `focus` | `VoidFunction` | Set focus on the selected tab trigger |
| `selectNext` | `(fromValue?: string) => void` | Selects the next tab |
| `selectPrev` | `(fromValue?: string) => void` | Selects the previous tab |
| `getTriggerState` | `(props: TriggerProps) => TriggerState` | Returns the state of the trigger with the given props |


## Accessibility

Complies with the [Tabs WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/).

### Keyboard Support

**`Tab`**
Description: When focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content.

**`ArrowDown`**
Description: Moves focus to the next trigger in vertical orientation and activates its associated content.

**`ArrowRight`**
Description: Moves focus to the next trigger in horizontal orientation and activates its associated content.

**`ArrowUp`**
Description: Moves focus to the previous trigger in vertical orientation and activates its associated content.

**`ArrowLeft`**
Description: Moves focus to the previous trigger in horizontal orientation and activates its associated content.

**`Home`**
Description: Moves focus to the first trigger and activates its associated content.

**`End`**
Description: Moves focus to the last trigger and activates its associated content.

**`Enter + Space`**
Description: In manual mode, when a trigger is focused, moves focus to its associated content.