TabGroup
OrganismAccessible tab navigation following the role="tablist" / role="tab" / role="tabpanel" ARIA pattern. Arrow-key navigation; tabIndex=-1 on inactive tabs. Optional per-tab isolation (error boundary + Suspense), `order` and per-tab `lazy` for tabs other modules contribute.
Profile settings content.
Security settings content.
Billing settings content.
<TabGroup
label="Account settings"
tabs={[
{ id: 'profile', label: 'Profile', content: <ProfileSettings /> },
{ id: 'security', label: 'Security', content: <SecuritySettings /> },
{ id: 'billing', label: 'Billing', content: <BillingSettings /> },
]}
/>Overview content.
Analytics content.
Reports content.
Settings disabled.
<TabGroup tabs={[
{ id: 'overview', label: 'Overview', icon: <BarChart2 />, content: ... },
{ id: 'analytics', label: 'Analytics', icon: <TrendingUp />, badge: <Badge>New</Badge>, content: ... },
{ id: 'settings', label: 'Settings', disabled: true, content: ... },
]} />Always mounted (initial).
Mount log: Tab 1 mounted
// With lazy=true, panel content is only rendered when first activated:
<TabGroup lazy tabs={[
{ id: 'heavy', label: 'Heavy', content: <HeavyComponent /> },
]} />Host tab: contact details.
Reported: nothing yet (open Calls)
// Each isolated panel sits in an error boundary + Suspense: a throwing tab shows a
// "failed" placeholder with Retry (or renderError), the others keep working.
<TabGroup
isolate
onTabError={(tabId, error) => report(tabId, error)}
tabs={[
{ id: 'overview', label: 'Overview', content: <Overview /> }, // host: order 0
{ id: 'tasks', label: 'Tasks', order: 10, lazy: true, content: <Tasks /> },
{ id: 'calls', label: 'Calls', order: 20, lazy: true, content: <CallLog /> },
]}
/>