UnderlinePanels

The UnderlinePanels are used to break related content into tabbed panels.

Page navigation navigation

React
experimental
Rails
ready

Underlined panels let users switch between 2 or more related panels of content without changing the URL or leaving their current context.

If you want to use this pattern for tabs that change the URL when activated, use the UnderlineNav component instead.

React examples

Default

Panel 1

With counter badges

Panel 1

With leading icons

Panel 1

More code examples

The previous examples are a curated subset of the full capabilities (such as reponsive behavior) of the UnderlinePanels component. For more examples, see the UnderlinePanels Storybook stories.

Props

UnderlinePanels

NameDefaultDescription
aria-label
string

Accessible name for the tab list

aria-labelledby
string

ID of the element containing the name for the tab list

value
string

The value of the selected tab, keyed to each UnderlinePanels.Tab/UnderlinePanels.Panel value. Provide this (with onChange) for a controlled component where the selected tab is the single source of truth. Requires the primer_react_underline_panels_controlled feature flag.

defaultValue
string

The value of the tab selected by default, for an uncontrolled component. Cannot be combined with value.

onChange
({value}: {value: string}) => void

Callback fired whenever the selected tab changes, for every selection method — pointer, Enter/Space, and Arrow/Home/End keys. Unlike UnderlinePanels.Tab's onSelect (which only fires on click and Enter/Space), this makes the selected value usable as a single source of truth for data-driven tabs.

activationMode
'automatic'
'automatic' | 'manual'

Controls how tabs are activated with the keyboard. 'automatic' selects on focus (Arrow/Home/End select immediately); 'manual' moves focus only and commits selection on Enter, Space, or click. Prefer 'manual' when displaying a panel is not instant (e.g. it triggers a network request). Requires the primer_react_underline_panels_controlled feature flag.

children Required
Array<UnderlinePanels.Tab | UnderlinePanels.Panel>

Tabs (UnderlinePanels.Tab) and panels (UnderlinePanels.Panel) to render

id
string

Custom string to use when generating the IDs of tabs and aria-labelledby for the panels

loadingCounters
false
boolean

Loading state for all counters. It displays loading animation for individual counters until all are resolved. It is needed to prevent multiple layout shift.

as
'div'
React.ElementType

The HTML element or React component used to render the outermost element.

UnderlinePanels.Tab

NameDefaultDescription
value
string

A value that uniquely identifies this tab, paired with the UnderlinePanels.Panel of the same value. Provide this to use domain values (e.g. 'branch') with the container's controlled value/onChange API. When omitted, tabs and panels are paired by DOM order.

aria-selected
false
| boolean | 'true' | 'false'

Whether this is the selected tab. For more information about aria-selected, see MDN.

onSelect
(event) => void

The handler that gets called when the tab is selected. Note: this fires only on click and Enter/Space — not on arrow-key navigation. For a callback that fires for every selection method, use the container's onChange prop instead.

counter
number | string

Content of CounterLabel rendered after tab text label

icon
Component

Icon rendered before the tab text label

UnderlinePanels.Panel

NameDefaultDescription
value
string

A value that uniquely identifies this panel, paired with the UnderlinePanels.Tab of the same value. When omitted, tabs and panels are paired by DOM order.

See div for more prop options