Skip to main content

Tabs

Used to display a list of tabs that can be used to navigate between different sections of a page.

Anatomy

  1. Tab Wrapper
  2. Tab List
  3. Tab
  4. Tab Panel

Import

import { TabsProvider, TabsList, Tab, TabsPanel } from '@pluralsight/react'

Usage

Tab Accessibility

In order for the Tabs be accessible, you must provide a matching id and value for each Tab that is unique to the Tab list.

Static Tabs

When you want to display set of Tabs that use static content, you can render all the components directly.

Result
Loading...
Live Editor

Dynamic Tabs

When you want to display a set of Tabs that use dynamic content, you can use the useTabs hook to render the Tabs and Panels.

Automatic loading states

When fetching data for a Tab Panel, the Tabs will automatically display a loading state until the data is available. This is because we utilize Supsense under the hood.

import { lazy, useMemo } from 'react'
import {
  TabsProvider,
  TabsList,
  Tab,
  useTabs,
  For,
  Show,
} from '@pluralsight/react'
import StaticPanel from './StaticPanel'

// Lazy load DynamicPanel for performance
const LazySlowPanel = lazy(() => import('./DynamicPanel'))

function PanelList() {
  const { activeTab } = useTabs()

  return (
    <Show
      when={activeTab !== 'tab-2'}
      fallback={<LazySlowPanel id="panel-2" labelledBy="tab-2" />}
    >
      <StaticPanel
        id={`panel-${activeTab.split('-')[0]}`}
        labelledBy={activeTab}
      >
        <p>{activeTab} Content</p>
      </StaticPanel>
    </Show>
  )
}

export default function App() {
  const tabs = useMemo(
    () => [
      {
        id: 'tab-1',
        label: 'Tab 1',
        panel: 'panel-1',
      },
      {
        id: 'tab-2',
        label: 'Tab 2',
        panel: 'panel-2',
      },
      {
        id: 'tab-3',
        label: 'Tab 3',
        panel: 'panel-3',
      },
    ],
    [],
  )

  return (
    <TabsProvider defaultActiveTab={tabs[0].id}>
      <TabsList>
        <For each={tabs}>
          {(tab) => (
            <Tab key={tab.id} controls={tab.panel} id={tab.id} value={tab.id}>
              {tab.label}
            </Tab>
          )}
        </For>
      </TabsList>

      <PanelList />
    </TabsProvider>
  )
}

Disabled Tabs

When you want to disable a Tab, you can use the disabled prop on the Tab component.

Result
Loading...
Live Editor

Custom Tabs Components

When you want to customize the Tab family of components, you can use any of the native React features to overwrite styling for the elements.

import { TabsProvider, Tab, type TabProps } from '@pluralsight/react'
import './custom.css'

function CustomTab(props: TabProps) {
  return (
    <Tab {...props} className="custom-tab">
      Custom Tab element
    </Tab>
  )
}

export default function App() {
  return (
    <TabsProvider>
      <CustomTab id="tab-1" value="tab-1" />
    </TabsProvider>
  )
}

Headless Styles

When you want to create a set of your own Tab components from scratch, you can use the Headless Styles API along with the React Aria to make them accessible.

A custom TabsList component
import {
getTabWrapperStyles,
getTabListStyles,
splitClassNameProp,
} from '@pluralsight/headless-styles'
import {
useAriaTabList,
type UseTabListOptions,
} from '@pluralsight/react-aria'

interface CustomTabListProps extends HTMLAttributes<HTMLDivElement>, UseTabListOptions {
activeTab: string,
}

export function CustomTabList(props: CustomTabListProps) {
const { activeTab, labelledBy, setFocus, tabsRefList, ...nativeProps } = props
const pandoStyles = getTabListStyles({
classNames: splitClassNameProp(nativeProps.className),
})
const ariaProps = useAriaTabList({
activeTabValue: ,
labelledBy,
setFocus: onTabClick,
tabsRefList,
})

return <div {...nativeProps} {...pandoStyles} {...ariaProps} />
}

Behavoir

Patterns

Do use Tabs when you need to display different content relative to the same page.

Don't use Tabs to navigate to different areas of a page or app.

Do keep the Tabs restricted to a single row.

Don't use present multiple rows of Tabs.

Usage

Do restrict the use of Tabs to once per page.

Don't use multiple Tabs within the same page.

Do keep the Tabs located on top of the panel content it is displaying.

Don't position the Tabs or realign them outside of the default top-left positioning.

API

TabsProvider

The TabsProvider component is used to provide the context for the Tabs components. It is required to be used in order for the Tabs components to work.

Props

PropTypeDescription
defaultActiveTabstringThe default active tab value. This is used to set the initial active tab.

TabsList

The TabsList component is used to wrap the Tab components. It is required to be used in order for the Tabs components to work.

Props

PropTypeDescription
labelledBystringThe id of a Form Label associated with the Tab group.

Tab

The Tab component is used to represent a single tab within the TabsList component.

Props

PropTypeDescription
controlsstringThe id of the TabsPanel component that the Tab controls.
disabledbooleanWhether or not the Tab is disabled.
idstringThe id of the Tab.
valuestringThe value of the Tab which should match the id.

TabsPanel

The TabsPanel component is used to represent the content of a single tab within the TabsList component.

Props

PropTypeDescription
labelledBystringThe id of the Tab component that the Tab Panel is associated with.
idstringThe id of the Tab Panel.

Accessibility

The Tabs component is built to be accessible to screen readers and keyboard users alike.

Screen Reader Interactions

Screen readers will announce the following:

  1. The number of Tabs
  2. The selected Tab
  3. The Tab Panel content

Keyboard Interactions

KeyDescription
TabMoves focus to the next focusable element.
Shift + TabMoves focus to the previous focusable element.
Left ArrowMoves focus to the previous Tab.
Right ArrowMoves focus to the next Tab.
HomeMoves focus to the first Tab.
EndMoves focus to the last Tab.
SpaceSelects the current Tab.
EnterSelects the current Tab.