Tabs
Fully accessible tab panels with ARIA roles — swap between views without leaving the page.
What it does
Tabs renders a role="tablist" button bar and the matching role="tabpanel"for the active tab. State is managed internally — you only need to supply the data. The component uses the first tab's id as the default unless you specify defaultTab.
Props
tabs— an array of objects, each withid(string),label(string), andcontent(ReactNode). Required.defaultTab— theidof the tab that should be active on first render. Defaults to the first tab's id.
Usage
import Tabs, { type Tab } from "@/components/Tabs";
const tabs: Tab[] = [
{ id: "monthly", label: "Monthly", content: <MonthlyPlans /> },
{ id: "yearly", label: "Yearly", content: <YearlyPlans /> },
];
<Tabs tabs={tabs} defaultTab="monthly" />The content field accepts any JSX, so you can pass entire React components, plain text, or a mix. Non-active panels are hidden with the HTML hidden attribute so they stay in the DOM — screen readers can still navigate to them.
TipKeep tab labels short — ideally one or two words. Long labels break the pill shape on small screens. If you need more than four tabs, consider a different navigation pattern.
TipThe
Pricing component uses Tabs internally to switch between monthly and yearly billing. Look at src/components/Pricing.tsx for a real-world example of how to wire tab state to price display.