Components
Tabs

Tabs

Flexible navigation tool with various modes and features.

Know React? Check out Solid!

You can explore the tabs component in the following curated examples.

Anatomy

To set up the tabs correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Examples

Learn how to use the Tabs component in your project. Let's take a look at the most basic example:

import { Tabs } from '@ark-ui/react/tabs'

export const Basic = () => (
  <Tabs.Root>
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</Tabs.Content>
  </Tabs.Root>
)

Initial Tab

To set a default tab on initial render, use the defaultValue prop:

import { Tabs } from '@ark-ui/react/tabs'

export const InitialTab = () => (
  <Tabs.Root defaultValue="react">
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</Tabs.Content>
  </Tabs.Root>
)

Tab Indicator

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

import { Tabs } from '@ark-ui/react/tabs'

export const Indicator = () => (
  <Tabs.Root>
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
      <Tabs.Indicator />
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</Tabs.Content>
  </Tabs.Root>
)

Lazy Mounting

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.

import { Tabs } from '@ark-ui/react/tabs'

export const LazyMount = () => (
  <Tabs.Root lazyMount unmountOnExit>
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
      <Tabs.Indicator />
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</Tabs.Content>
  </Tabs.Root>
)

Disabled Tab

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

import { Tabs } from '@ark-ui/react/tabs'

export const DisabledTab = () => (
  <Tabs.Root defaultValue="react">
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue" disabled>
        Vue
      </Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</Tabs.Content>
  </Tabs.Root>
)

Controlled Tabs

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:

import { Tabs } from '@ark-ui/react/tabs'
import { useState } from 'react'

export const Controlled = () => {
  const [value, setValue] = useState<string | null>('react')
  return (
    <Tabs.Root value={value} onValueChange={(e) => setValue(e.value)}>
      <Tabs.List>
        <Tabs.Trigger value="react">React</Tabs.Trigger>
        <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
        <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="react">React Content</Tabs.Content>
      <Tabs.Content value="vue">Vue Content</Tabs.Content>
      <Tabs.Content value="solid">Solid Content</Tabs.Content>
    </Tabs.Root>
  )
}

Router Controlled Tabs

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

import { Tabs } from '@ark-ui/react/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>
  )
}

Vertical Tabs

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

import { Tabs } from '@ark-ui/react/tabs'

export const Vertical = () => (
  <Tabs.Root orientation="vertical" defaultValue="react">
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</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

Enter key or by clicking on the tab.

import { Tabs } from '@ark-ui/react/tabs'

export const Manual = () => (
  <Tabs.Root activationMode="manual" defaultValue="react">
    <Tabs.List>
      <Tabs.Trigger value="react">React</Tabs.Trigger>
      <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
      <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content value="react">React Content</Tabs.Content>
    <Tabs.Content value="vue">Vue Content</Tabs.Content>
    <Tabs.Content value="solid">Solid Content</Tabs.Content>
  </Tabs.Root>
)

Using the Root Provider

The RootProvider component provides a context for the tabs. It accepts the value of the useTabs hook. You can leverage it to access the component state and methods from outside the tabs.

import { Tabs, useTabs } from '@ark-ui/react/tabs'

export const RootProvider = () => {
  const tabs = useTabs()

  return (
    <>
      <button onClick={() => tabs.focus()}>Focus</button>

      <Tabs.RootProvider value={tabs}>
        <Tabs.List>
          <Tabs.Trigger value="react">React</Tabs.Trigger>
          <Tabs.Trigger value="vue">Vue</Tabs.Trigger>
          <Tabs.Trigger value="solid">Solid</Tabs.Trigger>
        </Tabs.List>
        <Tabs.Content value="react">React Content</Tabs.Content>
        <Tabs.Content value="vue">Vue Content</Tabs.Content>
        <Tabs.Content value="solid">Solid Content</Tabs.Content>
      </Tabs.RootProvider>
    </>
  )
}

If you're using the RootProvider component, you don't need to use the Root component.

API Reference

Root

PropDefaultType
activationMode'automatic'
'manual' | 'automatic'

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
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
composite
boolean

Whether the tab is composite

defaultValue
string

The initial value of the tabs when it is first rendered. Use when you do not need to control the state of the tabs.

deselectable
boolean

Whether the active tab can be deselected when clicking on it.

id
string

The unique identifier of the machine.

ids
Partial<{ root: string trigger: string list: string content: string indicator: string }>

The ids of the elements in the tabs. Useful for composition.

lazyMountfalse
boolean

Whether to enable lazy mounting

loopFocustrue
boolean

Whether the keyboard navigation will loop from last tab to first, and vice versa.

onFocusChange
(details: FocusChangeDetails) => void

Callback to be called when the focused tab changes

onValueChange
(details: ValueChangeDetails) => void

Callback to be called when the selected/active tab changes

orientation'horizontal'
'horizontal' | 'vertical'

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
IntlTranslations

Specifies the localized strings that identifies the accessibility elements and their states

unmountOnExitfalse
boolean

Whether to unmount on exit.

value
string

The selected tab id

Data AttributeValue
[data-scope]tabs
[data-part]root
[data-orientation]The orientation of the tabs
[data-focus]Present when focused

TabContent

PropDefaultType
value
string

The value of the tab

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

TabIndicator

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

TabList

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

TabTrigger

PropDefaultType
value
string

The value of the tab

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
disabled
boolean

Whether the tab is disabled

RootProvider

PropDefaultType
value
UseTabsReturn

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
lazyMountfalse
boolean

Whether to enable lazy mounting

unmountOnExitfalse
boolean

Whether to unmount on exit.

Accessibility

Complies with the Tabs WAI-ARIA design pattern.

Keyboard Support

KeyDescription
Tab
When focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content.
ArrowDown
Moves focus to the next trigger in vertical orientation and activates its associated content.
ArrowRight
Moves focus to the next trigger in horizontal orientation and activates its associated content.
ArrowUp
Moves focus to the previous trigger in vertical orientation and activates its associated content.
ArrowLeft
Moves focus to the previous trigger in horizontal orientation and activates its associated content.
Home
Moves focus to the first trigger and activates its associated content.
End
Moves focus to the last trigger and activates its associated content.
EnterSpace
In manual mode, when a trigger is focused, moves focus to its associated content.