Skip to content

Tabs

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.

Language
Appearance

Sections with counts

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

  • Mon 14 · Annual checkup
  • Thu 17 · Blood test
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.

Follow-up with Dr. Ruiz, Monday at 9:30.

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.

1,240 contacts from the March list.

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, which the library’s tests render too.

Type to search…

↑↓ navigate↵ selectEsc close