---
title: "Tabs"
description: "Sections of one page or record. Arrow keys move between tabs, the thumb slides to the selection and the panel height follows its content. Keep the selected tab in the URL; give tabs an href when each section is its own route."
---

> Documentation Index
> Fetch the complete documentation index at: https://epidermis.getarbol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tabs

## Sections with counts

The simple form: a tabs list, and one TabPanel per tab. A count of 0 is hidden.

```tsx
import { useState } from 'react';
import { TabPanel, Tabs } from '@arbol-ai/ui';

const PatientTabs = () => {
  const [tab, setTab] = useState('appointments');
  return (
    <Tabs
      label="Patient sections"
      onChange={setTab}
      tabs={[
        { id: 'appointments', label: 'Appointments', count: 12 },
        { id: 'notes', label: 'Notes', count: 3 },
        { id: 'files', label: 'Files', count: 0 },
      ]}
      value={tab}
    >
      <TabPanel id="appointments">
        <ul className="space-y-1 text-sm">
          <li>Mon 14 · Annual checkup</li>
          <li>Thu 17 · Blood test</li>
        </ul>
      </TabPanel>
      <TabPanel id="notes">
        <p className="text-sm">Prefers morning appointments. Allergic to penicillin.</p>
      </TabPanel>
      <TabPanel id="files">
        <p className="text-muted-foreground text-sm">No files yet.</p>
      </TabPanel>
    </Tabs>
  );
};

<PatientTabs />
```

## Icons and a kept draft

keepMounted keeps a panel (and the note typed in it) while another tab shows, hidden and out of reach.

```tsx
import { CalendarBlankIcon, FileTextIcon, NotePencilIcon } from '@phosphor-icons/react';
import { TabPanel, Tabs } from '@arbol-ai/ui';

<Tabs
  label="Contact sections"
  tabs={[
    { id: 'appointments', label: 'Appointments', icon: <CalendarBlankIcon aria-hidden /> },
    { id: 'notes', label: 'Notes', icon: <NotePencilIcon aria-hidden /> },
    { id: 'files', label: 'Files', icon: <FileTextIcon aria-hidden />, isDisabled: true },
  ]}
>
  <TabPanel id="appointments">
    <p className="text-sm">Follow-up with Dr. Ruiz, Monday at 9:30.</p>
  </TabPanel>
  <TabPanel id="notes" keepMounted>
    <textarea
      aria-label="New note"
      className="rounded-field w-full border p-2 text-sm"
      placeholder="Write a note…"
    />
  </TabPanel>
  <TabPanel id="files">No files.</TabPanel>
</Tabs>
```

## Composed

TabList, Tab, TabPanels and TabPanel, for tabs with content of their own.

```tsx
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@arbol-ai/ui';

<Tabs defaultValue="recipients">
  <TabList aria-label="Campaign sections">
    <Tab id="recipients">
      Recipients{' '}
      <span className="bg-brand-tint text-brand-accent rounded-full px-1.5 text-xs">New</span>
    </Tab>
    <Tab id="schedule">Schedule</Tab>
  </TabList>
  <TabPanels>
    <TabPanel id="recipients">
      <p className="text-sm">1,240 contacts from the March list.</p>
    </TabPanel>
    <TabPanel id="schedule">
      <p className="text-sm">Weekdays from 9:00 to 17:00.</p>
    </TabPanel>
  </TabPanels>
</Tabs>
```

## Source

The examples above are [`src/components/tabs/tabs.examples.tsx`](https://github.com/getarbol/project-arbol-ui/blob/main/src/components/tabs/tabs.examples.tsx), which the library's tests render too.

Source: https://epidermis.getarbol.com/components/navigation/tabs/index.mdx
