Tabs

Navigate between different sections of content.

Documentation
React Aria
Pattern
W3C ARIA
Source
GitHub
Issues
Report
Your profile information.
Tabs example
import { GearSix, User } from "@phosphor-icons/react/dist/ssr";
import { Tab, TabList, TabPanel, Tabs } from "@/shim-ui/tabs";

export default () => (
  <Tabs>
    <TabList>
      <Tab id="profile">
        <User size={16} weight="duotone" />
        Profile
      </Tab>
      <Tab id="settings">
        <GearSix size={16} weight="duotone" />
        Settings
      </Tab>
    </TabList>

    <TabPanel className="py-4" id="profile">
      Your profile information.
    </TabPanel>
    <TabPanel className="py-4" id="settings">
      Change your settings.
    </TabPanel>
  </Tabs>
);

Install

Use the CLI or copy the source code manually.

pnpm dlx @kkga/shim add tabs

Variant

Set the variant prop on <TabList> to adjust tab styling.

Variant example
import { Tab, TabList, Tabs } from "@/shim-ui/tabs";

export default () => (
  <>
    <Tabs>
      <TabList variant="soft">
        <Tab id="profile">Profile</Tab>
        <Tab id="settings">Settings</Tab>
      </TabList>
    </Tabs>
    <Tabs>
      <TabList variant="underline">
        <Tab id="profile">Profile</Tab>
        <Tab id="settings">Settings</Tab>
      </TabList>
    </Tabs>
  </>
);

Size

Use the size prop on <TabList> to control tab spacing and typography.

Size example
import { Tab, TabList, Tabs } from "@/shim-ui/tabs";

export default () => (
  <>
    <Tabs>
      <TabList size={1}>
        <Tab id="profile">Profile</Tab>
        <Tab id="settings">Settings</Tab>
      </TabList>
    </Tabs>
    <Tabs>
      <TabList size={2}>
        <Tab id="profile">Profile</Tab>
        <Tab id="settings">Settings</Tab>
      </TabList>
    </Tabs>
    <Tabs>
      <TabList size={3}>
        <Tab id="profile">Profile</Tab>
        <Tab id="settings">Settings</Tab>
      </TabList>
    </Tabs>
    <Tabs>
      <TabList size={4}>
        <Tab id="profile">Profile</Tab>
        <Tab id="settings">Settings</Tab>
      </TabList>
    </Tabs>
  </>
);

Orientation

Switch between horizontal and vertical layouts via theorientation prop.

Your profile.
Orientation example
import { GearSixIcon, UserIcon } from "@phosphor-icons/react/dist/ssr";
import { Tab, TabList, TabPanel, Tabs } from "@/shim-ui/tabs";

export default () => (
  <Tabs orientation="vertical">
    <TabList className="w-[120px]">
      <Tab id="profile">
        <UserIcon size={16} weight="duotone" />
        Profile
      </Tab>
      <Tab id="settings">
        <GearSixIcon size={16} weight="duotone" />
        Settings
      </Tab>
    </TabList>

    <TabPanel className="px-4 py-2" id="profile">
      Your profile.
    </TabPanel>
    <TabPanel className="px-4 py-2" id="settings">
      Your settings.
    </TabPanel>
  </Tabs>
);

Controlled

Manage tab selection with selectedKey andonSelectionChange.

Selected tab: favorites

Controlled example
"use client";

import { useState } from "react";
import type { Key } from "react-aria-components";
import { Tab, TabList, Tabs } from "@/shim-ui/tabs";

export default () => {
  const [tabId, setTabId] = useState<Key>("favorites");

  return (
    <>
      <p>Selected tab: {tabId}</p>
      <Tabs onSelectionChange={setTabId} selectedKey={tabId}>
        <TabList>
          <Tab id="recent">Recent</Tab>
          <Tab id="favorites">Favorites</Tab>
        </TabList>
      </Tabs>
    </>
  );
};

Dynamic content

Render tabs from dynamic data by passing an items iterable and a render function as children.

Your profile information.
Dynamic content example
"use client";

import { GearSix, User, XSquare } from "@phosphor-icons/react";
import { useState } from "react";
import { Collection, type Key } from "react-aria-components";
import { Tab, TabList, TabPanel, Tabs } from "@/shim-ui/tabs";

export default () => {
  const [tabId, setTabId] = useState<Key>("profile");
  const items = [
    {
      id: "profile",
      label: "Profile",
      icon: User,
      content: "Your profile information.",
      disabled: false,
    },
    {
      id: "settings",
      label: "Settings",
      icon: GearSix,
      content: "Change your settings here.",
      disabled: false,
    },
    {
      id: "disabled",
      label: "Disabled",
      icon: XSquare,
      content: "You can't see this.",
      disabled: true,
    },
  ];

  return (
    <Tabs
      disabledKeys={items
        .filter((item) => item.disabled)
        .map((item) => item.id)}
      onSelectionChange={setTabId}
      selectedKey={tabId}
    >
      <TabList items={items}>
        {({ id, icon: Icon, label }) => (
          <Tab aria-label={label} id={id}>
            <Icon size={16} weight="duotone" />
          </Tab>
        )}
      </TabList>

      <Collection items={items}>
        {({ content }) => <TabPanel className="py-4">{content}</TabPanel>}
      </Collection>
    </Tabs>
  );
};