Switches between related panels of content in the same place. Use with TabList, Tab and TabPanel.
import { Tab, TabList, TabPanel, Tabs } from "@rdloom/react";
export default function TabsUnderlineExample() {
return (
<Tabs className="w-full max-w-lg">
<TabList aria-label="Project">
<Tab id="overview">Overview</Tab>
<Tab id="activity">Activity</Tab>
<Tab id="settings">Settings</Tab>
</TabList>
<TabPanel id="overview">Project summary and key numbers.</TabPanel>
<TabPanel id="activity">Recent changes by the team.</TabPanel>
<TabPanel id="settings">Name, members and permissions.</TabPanel>
</Tabs>
);
}Installation
npx rdloom add tabsCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/tabs.jsonWorks, but without upgrade tracking.
Usage
import { Tab, TabList, TabPanel, Tabs } from "@rdloom/react";
<Tabs className="w-full max-w-lg">
<TabList aria-label="Project">
<Tab id="overview">Overview</Tab>
<Tab id="activity">Activity</Tab>
<Tab id="settings">Settings</Tab>
</TabList>
<TabPanel id="overview">Project summary and key numbers.</TabPanel>
<TabPanel id="activity">Recent changes by the team.</TabPanel>
<TabPanel id="settings">Name, members and permissions.</TabPanel>
</Tabs>Pill
import { Tab, TabList, TabPanel, Tabs } from "@rdloom/react";
export default function TabsPillExample() {
return (
<Tabs variant="pill">
<TabList aria-label="Project">
<Tab id="overview">Overview</Tab>
<Tab id="activity">Activity</Tab>
<Tab id="settings">Settings</Tab>
</TabList>
<TabPanel id="overview">Project summary and key numbers.</TabPanel>
<TabPanel id="activity">Recent changes by the team.</TabPanel>
<TabPanel id="settings">Name, members and permissions.</TabPanel>
</Tabs>
);
}Vertical
import { Tab, TabList, TabPanel, Tabs } from "@rdloom/react";
export default function TabsVerticalExample() {
return (
<Tabs orientation="vertical" className="w-full max-w-lg">
<TabList aria-label="Project">
<Tab id="overview">Overview</Tab>
<Tab id="activity">Activity</Tab>
<Tab id="settings">Settings</Tab>
</TabList>
<TabPanel id="overview">Project summary and key numbers.</TabPanel>
<TabPanel id="activity">Recent changes by the team.</TabPanel>
<TabPanel id="settings">Name, members and permissions.</TabPanel>
</Tabs>
);
}Controlled
Showing: week
import { useState } from "react";
import { Tab, TabList, TabPanel, Tabs } from "@rdloom/react";
export default function TabsControlledExample() {
const [tab, setTab] = useState("week");
return (
<div className="flex w-full max-w-lg flex-col gap-2">
<Tabs selectedKey={tab} onSelectionChange={(key) => setTab(key as string)}>
<TabList aria-label="Range">
<Tab id="day">Day</Tab>
<Tab id="week">Week</Tab>
<Tab id="month">Month</Tab>
</TabList>
<TabPanel id="day">Today's numbers.</TabPanel>
<TabPanel id="week">This week's numbers.</TabPanel>
<TabPanel id="month">This month's numbers.</TabPanel>
</Tabs>
<p className="text-sm">Showing: {tab}</p>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
variantVisual style of the tab list. | "underline" | "pill" | "underline" |
orientationDirection of the tab list. | "horizontal" | "vertical" | "horizontal" |
selectedKeyControlled selected tab. | Key | none |
defaultSelectedKeyInitial tab when uncontrolled. | Key | none |
isDisabledDisables every tab. | boolean | false |
childrenrequiredA TabList followed by TabPanels. | node | none |
onSelectionChangeCalled with the newly selected tab key. | (key: Key) => void | none |
Accessibility
Role tablist, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Arrow keys move between tabs
- Home/End jump to first/last tab
- Tab moves into the panel
Screen readers announce
- Each tab: tab, its name, selected state and position (1 of 3)
- Switching tabs announces the new tab; the panel's content is reachable next
What your code must do
- Each tab controls exactly one panel
- Tab list has an aria-label when there's no visible heading
Guidelines
Use it when
- Grouping related views of the same object, like Overview / Activity / Settings
Avoid it when
- Sequential steps: use a Stepper
- Page navigation: use links
Don't
- More than about 7 tabs
- Tabs whose content depends on another tab's input
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-action-primary--rd-color-border-default--rd-color-surface-subtle--rd-color-surface-raised--rd-color-text-default--rd-color-text-muted--rd-color-focus-ring--rd-radius-control