Skip to content

Tabs

Tabs group related page content to enable the quick discovery of information.
Figma logo
Anatomy of Tabs
Tabs and its sub-components
Anatomy of the Tabs component
ElementPurpose
Scroll buttonView hidden tabs to the left or right.
Tab textDescribes the content within a tab.
OverflowA fade behavior that appears when there are too many tabs to fit horizontally in a view.
Active tab indicatorA Guava-500 border under the tab text that indicates which tab is active.
DividerA component used to make consistent divider lines.
  • Tab Panels mount immediately
    • Consider leveraging Tab state to delay queries for inactive tabs

Resize these examples to preview mobile behavior.

Tabs adapt to viewports of different widths by determining the available space. As the UI is condensed, the tab’s container will adjust accordingly into an overflow container.

Tabs comply with WAI-ARIA authoring guides. They are accessible to screen readers and operable from a keyboard using automatic activation.

Navigating Tabs with a keyboard
KeysAction
TabWhen not focused on the tab container, place focus on the first tab.
When focused on a tab button, focus will be placed on its corresponding tab panel.
Left arrowChange focus to the previous tab.
If focus is on the first tab, move focus to the last tab.
Right arrowChange focus to the next tab. If focus is on the last tab, move focus to the first tab.

The IconButton component within tabs is removed from the tab order and hidden from screen readers.

Tabs is made up of four sub-components.

Tabs sub-components
SubcomponentPurpose
TabsThe outermost container and parent of the Tab, TabList, and TabPanel sub-components. This sub-component matches each Tab with its corresponding TabPanel based on written order.
TabListThe direct parent of all Tab elements. This sub-component is responsible for scroll behavior when screen space is limited, for event listening (clicking and focusing), as well as for the sliding highlight animation.
TabEach Tab has a corresponding TabPanel, which becomes visible when the Tab is selected.A Tab is a button, so it can be hovered, clicked, and disabled.
TabPanelEach TabPanel has a corresponding Tab, and the TabPanel becomes visible when its Tab is selected.

Tabs Props

React props
NameTypeDefaultDescription
children  ReactNodeThe content of the Tabs container.
value  stringThe value of the controlled selected Tab.

TabList Props

React props
NameTypeDefaultDescription
children  ReactNodeThe content of the TabList.
aria-label  RequiredstringA breakdown of what these tabs are about, used for screen reader accessibility.

Tab Props

React props
NameTypeDefaultDescription
children  ReactNodeThe content of the tab.
id  stringCustom ID for the Tab; used to associate it with its corresponding TabPanel.
value  stringAn indexing value to be used by Tabs value prop to indicate controlled selection.
onClick  functionA function to handle controlled selected tab change.

TabPanel Props

React props
NameTypeDefaultDescription
children  ReactNodeThe content of the tab.
id  stringCustom ID for the TabPanel; used to associate it with its corresponding Tab.

The following testing snippet(s) offer suggestions for testing the component using React Testing Library with occasional help from Jest.

const TestTabs = () => {
return (
<Tabs>
<TabList aria-label="Payroll and HR info">
<Tab>Employees (12)</Tab>
<Tab>Departments (2)</Tab>
<Tab>Contractors</Tab>
</TabList>
<TabPanel>Employees panel.</TabPanel>
<TabPanel>Departments panel.</TabPanel>
<TabPanel>Contractors panel.</TabPanel>
</Tabs>
);
};
render(<TestTabs />);
const tablist = screen.getByLabelText(/Payroll and HR info/);
const employeesTab = within(tablist).getByRole('tab', { name: 'Employees (12)' });
const departmentsTab = within(tablist).getByRole('tab', { name: 'Departments' });
const contractorsTab = within(tablist).getByRole('tab', { name: 'Contractors (2)' });
const employeesTabPanel = screen.getByRole('tabpanel', { name: /Employees/ });
const departmentsTabPanel = screen.getByText(/Departments panel./);
const contractorsTabPanel = screen.getByText(/Contractors panel./);
// Click Employees tab, Employees tabpanel is visible amd Departments tabpanel is not
act(() => {
userEvent.click(employeesTab);
});
expect(employeesTabPanel).toBeVisible();
expect(departmentsTabPanel).not.toBeVisible();
expect(contractorsTabPanel).not.toBeVisible();
// Click Departments tab, Departments tabpanel is visible amd Employees tabpanel is not
act(() => {
userEvent.click(departmentsTab);
});
expect(employeesTabPanel).not.toBeVisible();
expect(departmentsTabPanel).toBeVisible();
expect(contractorsTabPanel).not.toBeVisible();
// Click Contractors tab, Departments tabpanel is visible amd Employees tabpanel is not
act(() => {
userEvent.click(contractorsTab);
});
expect(employeesTabPanel).not.toBeVisible();
expect(departmentsTabPanel).not.toBeVisible();
expect(contractorsTabPanel).toBeVisible();