Tabs

A horizontal tab navigation component with full keyboard support and ARIA compliance. Tabs can render as links or buttons and support custom styling, add-ons, and label rendering.

Props

Tabs

PropTypeDefaultDescription
aria-controlsstringRequiredID of the content panel this tab controls
tabsTab[]RequiredArray of tab configurations
variant"underline" | "segmented""underline"Visual selection treatment
labelSizeSize"large"Text size for tab labels
iconSizeSize"medium"Size of tab icons
tabPaddingPadding"medium"Padding inside each tab
gapGap"none"Space between tabs
jssTabJSS | (data) => JSS-Custom styles for tabs (can be conditional on selected state)
labelRenderer(label: string) => ReactNode-Custom label rendering function

Tab object

PropertyTypeDefaultDescription
labelstringRequiredTab display text
iconIconType-Icon displayed with the label
hrefstring-Link URL (renders as link if provided)
onClick() => void-Click handler (renders as button if no href)
selectedbooleanfalseWhether this tab is currently selected
addOnReactNode-Additional element (e.g., badge, icon)
addOnPosition"start" | "end""start"Position of add-on relative to label

Basic usage

<Tabs
  aria-controls="panel-1"
  tabs={[
    { href: "/Tabs", selected: true, label: "Tab 1" },
    { href: "/Tabs", selected: false, label: "Tab 2" },
    { href: "/Tabs", selected: false, label: "Tab 3" },
  ]}
/>

With add-ons

Add badges, icons, or other elements to tabs:

// Badge before label (default)
<Tabs
  aria-controls="panel"
  tabs={[
    { href: "/messages", selected: false, label: "Messages" },
    {
      href: "/notifications",
      selected: true,
      label: "Notifications",
      addOn: <Badge color="negative" size="small" />,
    },
  ]}
/>

// Badge after label
<Tabs
  aria-controls="panel"
  tabs={[
    {
      href: "/new",
      selected: true,
      label: "New",
      addOn: <Badge color="highlight" size="medium" />,
      addOnPosition: "end",
    },
  ]}
/>

Tabs as buttons

Omit href to render tabs as buttons. Useful for in-page tab switching:

const [activeTab, setActiveTab] = useState("details");

<Tabs
  aria-controls="content-panel"
  tabs={[
    {
      selected: activeTab === "details",
      label: "Details",
      onClick: () => setActiveTab("details"),
    },
    {
      selected: activeTab === "reviews",
      label: "Reviews",
      onClick: () => setActiveTab("reviews"),
    },
    {
      selected: activeTab === "related",
      label: "Related",
      onClick: () => setActiveTab("related"),
    },
  ]}
/>

Custom label rendering

Use labelRenderer to customize how tab labels are displayed:

<Tabs
  aria-controls="panel"
  labelRenderer={(label) => (
    <>
      <Icon icon="star" size="small" />
      {label}
    </>
  )}
  tabs={[
    { href: "/featured", selected: true, label: "Featured" },
    { href: "/popular", selected: false, label: "Popular" },
  ]}
/>

Custom tab styles

Use jssTab for custom styling. It can be a static style object or a function that receives the tab's selected state:

// Conditional styling based on selection
<Tabs
  aria-controls="panel"
  jssTab={(data) => ({
    borderBottom: data.selected
      ? "3px solid var(--highlight)"
      : "3px solid transparent",
    backgroundColor: data.selected
      ? "var(--light-highlight)"
      : "transparent",
  })}
  tabs={tabs}
/>

// Static styling for all tabs
<Tabs
  aria-controls="panel"
  jssTab={{ borderRadius: 8 }}
  tabs={tabs}
/>

Keyboard navigation

Tabs support full keyboard navigation:

  • Arrow Left/Right: Move focus between tabs
  • Enter/Space: Activate the focused tab
  • Home: Move to first tab
  • End: Move to last tab

Accessibility

The Tabs component follows WAI-ARIA tab patterns:

  • Container has role="tablist"
  • Each tab has role="tab" and aria-controls pointing to its panel
  • Selected tab has aria-selected="true"
  • Content panel should have role="tabpanel" and matching id
<Tabs
  aria-controls="settings-panel"
  tabs={tabs}
/>

<div id="settings-panel" role="tabpanel">
  {/* Panel content */}
</div>