Tabs

Flexible navigation tool with various modes and features.

Are you familiar with React? You can try out Solid.

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'

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'

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'

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'

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'

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'
import { useState } from 'react'

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'

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'

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>
)

API Reference

Root

PropTypeDefault
activationMode
'manual' | 'automatic'"automatic"
asChild
boolean
defaultValue
string
dir
'ltr' | 'rtl'"ltr"
getRootNode
() => Node | ShadowRoot | Document
id
string
ids
Partial<{ root: string trigger: string tablist: string content: string indicator: string }>
lazyMount
booleanfalse
loop
booleantrue
onExitComplete
() => void
onFocusChange
(details: FocusChangeDetails) => void
onValueChange
(details: ValueChangeDetails) => void
orientation
'horizontal' | 'vertical'"horizontal"
present
boolean
translations
IntlTranslations
unmountOnExit
booleanfalse
value
string

TabList

PropTypeDefault
asChild
boolean

TabContent

PropTypeDefault
value
string
asChild
boolean

TabTrigger

PropTypeDefault
value
string
asChild
boolean
disabled
boolean

TabIndicator

PropTypeDefault
asChild
boolean