Skip to main content
On this page

Tabs organise related content into parallel sections that users can switch between without leaving the page.

When to use​

  • To present two or more distinct views of the same subject, where each view is complete on its own and the user only needs one at a time
  • To reduce visual complexity on a page that contains a large amount of related content
  • To let the user switch context without navigating to a separate page

When not to use​

  • To show sequential steps or a wizard flow - tabs communicate parallel content, not progression. Use a separate page per step instead.
  • To compare content across sections - if the user needs to move back and forth between tabs to make a comparison, the content should be visible on screen at the same time. Use a Data Table or a Summary List instead.
  • To show one or two sections - the overhead of a tab strip is not worth it for only two sections where a simple heading would do. Use an Accordion or plain headings instead.
  • As primary navigation - tabs belong inside a page, not at the site or application level. Use the application's navigation system instead.
  • When section labels are long or numerous - tabs that wrap to a second row or truncate labels are harder to scan. If the labels do not fit comfortably in a single row, use an Accordion instead.

Anatomy​

  1. Tab list (the horizontal strip containing all tab buttons)
  2. Tab (an individual tab button; one is active at any time)
  3. Active indicator (the coloured bar at the bottom of the active tab)
  4. Tab panel (the content area associated with the active tab)

Keyboard support​

Keyboard functions​

  • Left arrow: move to the previous tab and activate it
  • Right arrow: move to the next tab and activate it
  • Tab: move focus into the active tab panel

Basic usage​

Loading...
Edit live code
<Tabs>
  <TabList>
    <Tab>Tab 1</Tab>
    <Tab>Tab 2</Tab>
    <Tab>Tab 3</Tab>
  </TabList>
  <TabPanel>
    <P>Tab 1 Content</P>
  </TabPanel>
  <TabPanel>
    <P>Tab 2 Content</P>
  </TabPanel>
  <TabPanel>
    <P>Tab 3 Content</P>
  </TabPanel>
</Tabs>

Variants and features​

Default selected tab​

By default the first tab is active. Use defaultSelected to open a different tab on first render, passing a zero-based index.

Loading...
Edit live code
<Tabs defaultSelected={1}>
  <TabList>
    <Tab>Billing periods</Tab>
    <Tab>Direct Debit</Tab>
    <Tab>Supplies</Tab>
  </TabList>
  <TabPanel>
    <P>Billing periods content</P>
  </TabPanel>
  <TabPanel>
    <P>Direct Debit content - selected on first render</P>
  </TabPanel>
  <TabPanel>
    <P>Supplies content</P>
  </TabPanel>
</Tabs>

Controlled tabs​

Use value and onSelected together to control the active tab from outside the component. This is useful when the active tab needs to be driven by external state, such as a URL parameter or a parent component.

onSelected fires with the zero-based index of the newly active tab. It does not fire on initial render.

Loading...
Edit live code
() => { // ignore - for live code example only.
  const Example = () => {
    const [selected, setSelected] = useState(0);

    return (
      <Stack spaceBetween={3}>
        <Tabs value={selected} onSelected={setSelected}>
          <TabList>
            <Tab>Billing periods</Tab>
            <Tab>Direct Debit</Tab>
            <Tab>Supplies</Tab>
          </TabList>
          <TabPanel>
            <P>Billing periods content</P>
          </TabPanel>
          <TabPanel>
            <P>Direct Debit content</P>
          </TabPanel>
          <TabPanel>
            <P>Supplies content</P>
          </TabPanel>
        </Tabs>
        <SecondaryCTAButton onClick={() => setSelected(2)}>
          Jump to Supplies
        </SecondaryCTAButton>
      </Stack>
    );
  }

  return <Example />;
}

Rich tab labels​

A Tab accepts any content, not just a text label. Keep labels short enough to stay scannable in a single row.

Loading...
Edit live code
<Tabs>
  <TabList>
    <Tab>
      Account<br />
      123456789<br />
      <Badge variant="success">Active</Badge>
    </Tab>
    <Tab>
      Account<br />
      234567890<br />
      <Badge>Inactive</Badge>
    </Tab>
  </TabList>
  <TabPanel>
    <P>Details for account 123456789</P>
  </TabPanel>
  <TabPanel>
    <P>Details for account 234567890</P>
  </TabPanel>
</Tabs>

Tab panel visibility​

Inactive tab panels are hidden using display: none. They remain in the DOM, so their content is preserved across tab switches. There is no lazy rendering. All panel content mounts on initial render.