Skip to content

Navigation

Tabs

A set of headings that swaps one panel at a time.

When to use it: Use it for peer sections where only one needs to be visible.

On this page

Example

A live component preview.
Tabs.tsxtsx
import { Button, Tab, TabList, TabPanel, Tabs } from "@comp0/react";

export function Example() {
  return (
    <Tabs as="div" defaultValue="preview">
      <TabList className="flex max-w-full gap-1 overflow-x-auto border-b border-zinc-950/10 dark:border-white/10">
        <Tab
          className="shrink-0 px-3 py-2.5 text-base text-zinc-700 data-selected:border-b-2 data-selected:border-teal-600 data-selected:text-teal-800 sm:py-2 sm:text-sm dark:text-zinc-300 dark:data-selected:border-teal-400 dark:data-selected:text-teal-200"
          tab="preview"
        >
          Preview
        </Tab>
        <Tab
          className="shrink-0 px-3 py-2.5 text-base text-zinc-700 data-selected:border-b-2 data-selected:border-teal-600 data-selected:text-teal-800 sm:py-2 sm:text-sm dark:text-zinc-300 dark:data-selected:border-teal-400 dark:data-selected:text-teal-200"
          tab="code"
        >
          Code
        </Tab>
      </TabList>
      <TabPanel
        className="pt-3 text-base text-zinc-700 sm:text-sm dark:text-zinc-300"
        tab="preview"
      >
        A live component preview.
      </TabPanel>
      <TabPanel
        className="pt-3 font-mono text-base text-zinc-700 sm:text-sm dark:text-zinc-300"
        tab="code"
      >
        import {"{ Button }"} from "@comp0/react"
      </TabPanel>
    </Tabs>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. Tabs

    Selected-tab provider. Does not add a DOM element.

  2. TabList

    Tab list element. Owns a DOM element.

  3. Tab

    Native button with tab role. Owns a DOM element.

  4. TabPanel

    Panel for a matching tab. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Start Tabs with a starting value.

  2. 2

    Add the supporting parts

    Give each Tab and its TabPanel the same tab id.

  3. 3

    Make the behavior clear

    Put the Tab elements together inside TabList.

    Exampletsx
    <Tabs defaultValue="one"><TabList><Tab tab="one">One</Tab></TabList><TabPanel tab="one">Panel one</TabPanel></Tabs>

Keyboard

Moves to next tab.
Moves to previous tab.
Home
Moves to first tab.
End
Moves to last tab.

Forms and accessibility

No native form behavior.

Accessibility checklist

  • Keep tab names short and distinct.
  • Ensure each Tab has a matching TabPanel key.
  • Do not use tabs to hide unrelated page navigation.

API reference

Importtsx
import { Tab, TabList, TabPanel, Tabs } from "@comp0/react";

Tabs

Context only

Selected-tab provider.

PropTypeDescription
valuestringControlled selected tab.
defaultValuestringInitial selected tab.
onChange(value: string) => voidReceives the next selected tab.

TabList

DOM element

Tab list element.

PropTypeDescription
aria-labelstringNames the tab list for assistive technology.
orientation"horizontal" | "vertical"Arrow-key axis and aria-orientation.

Tab

DOM element

Native button with tab role.

PropTypeDescription
tabstringIdentity that pairs this tab with its panel.
disabledbooleanDisables the tab.

TabPanel

DOM element

Panel for a matching tab.

PropTypeDescription
tabstringThe tab this panel belongs to.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

[data-selected]

on Tab, TabPanel

This tab or panel is selected.

Keep exploring