Tabs may be used navigate between multiple URLs. Tabs are intended as page-level navigation - if you're looking at just switching panels of content, please use SegmentedControl.

Figma:

Responsive:

Adaptive:

Props

Component props
Name
Type
Default
activeTabIndex
Required
number
-

The index of the active tab.

onChange
Required
(arg1: {
  event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>;
  readonly activeTabIndex: number;
  dangerouslyDisableOnNavigation: () => void;
}) => void
-

If your app requires client navigation, be sure to use GlobalEventsHandlerProvider and/or onChange to navigate instead of getting a full page refresh just using href.

tabs
Required
ReadonlyArray<{
  href: string;
  id?: string;
  indicator?: "dot" | number;
  ref?: {
    current: HTMLElement | null | undefined;
  };
  text: React.Node;
}>
-

The array of tabs to be displayed. The active tab (as indicated by activeTabIndex) will be underlined. Use the optional indicator field to show a notification of new items on the tab — see the indicator variant to learn more. Though text currently accepts a React.Node, this is deprecated and will be replaced by a simple string type soon.

bgColor
"default" | "transparent"
"default"

If Tabs is displayed in a container with a colored background, use this prop to remove the white tab background. See the background color example to learn more.

dataTestId
string
-

Available for testing purposes, if needed. Consider better queries before using this prop.

wrap
boolean
-

By default, tabs will all try to fit onto one line. Use this prop to allow the items to wrap onto multiple lines, from top to bottom.

Usage guidelines

When to use
  • To break up a large collection of content into logical, digestible views.
  • To switch between different, yet related views, such as Updates and Messages.
When not to use
  • When any UI or content above the Tabs is altered upon selection. Use Link instead.
  • To break up content that is not related to each other or is not on the same hierarchical level.

Best practices

Do

Place Tabs directly above the target content.

Don't

Use Tabs as a way to filter content. Consider using SegmentedControl in this use-case.

Do

Keep Tab labels concise, ideally one to two words.

Don't

Truncate labels in Tabs. If there is not enough horizontal space, allow the Tabs to scroll horizontally on mobile and touch surfaces. For desktop, wrap the group of tabs to multiple lines.

Do

Order Tabs by relevance — the first tab should be the most logical starting view. Ideally, sequence Tabs by association — tabs with similar content should be adjacent to each other.

Don't

Disable or hide Tabs if a Tab's content is empty. There should always be at least 2 Tabs. We don't support applying a disabled state for the Tab as it can cause usability and accessibility issues.

Accessibility

Tabs are intended for page-level navigation between multiple URLs. Each tab must have an individual title that precisely describes the tab content. Provide a short, descriptive label for screen-readers using accessibilityLabel. It is helpful for users of assistive technologies so they have the necessary information to navigate the content efficiently.

Keyboard

Tab key navigates the tabs.
Enter/return key activates a tab (i.e., it navigates to the link href).

Screen Reader

The tab/link must announce a state of "current" if the href matches the current window URL.

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

The Tab's title should be 3 words or less: long enough to be understood by users but short enough to prevent text wrapping. Aim for a single word when possible.

Variants

Wrapping

Wrapping to multiple lines is available for tight spaces on desktop interfaces where horizontal scrolling is harder and less accessible.

import { useState } from 'react';
import { Box, Flex, Label, Switch, Tabs, Text } from 'gestalt';

export default function Example() {
  const [activeIndex, setActiveIndex] = useState(0);
  const [wrap, setWrap] = useState(false);

  return (
    <Flex
      alignItems="center"
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Flex alignItems="start" direction="column" gap={{ column: 4, row: 0 }}>
        <Flex gap={{ row: 4, column: 0 }}>
          <Label htmlFor="wrap">
            <Text>Wrap</Text>
          </Label>
          <Switch id="wrap" onChange={() => setWrap(!wrap)} switched={wrap} />
        </Flex>

        <Box borderStyle="sm" maxWidth={500} overflow="auto" padding={1}>
          <Tabs
            activeTabIndex={activeIndex}
            onChange={({ activeTabIndex, event }) => {
              event.preventDefault();
              setActiveIndex(activeTabIndex);
            }}
            tabs={[
              { href: 'https://pinterest.com', text: 'Boards for You' },
              { href: 'https://pinterest.com', text: 'Pins for You' },
              { href: 'https://pinterest.com', text: 'Following' },
              { href: 'https://pinterest.com', text: 'People to Follow' },
            ]}
            wrap={wrap}
          />
        </Box>
      </Flex>
    </Flex>
  );
}

Indicator

Use the indicator field on individual tabs to indicate notifications. You can either show a red dot or a number — numbers greater than 99 will be shown as "99+".

import { useState } from 'react';
import { Flex, Tabs } from 'gestalt';

export default function Example() {
  const [activeIndex, setActiveIndex] = useState(0);

  return (
    <Flex
      alignItems="center"
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Tabs
        activeTabIndex={activeIndex}
        onChange={({ activeTabIndex, event }) => {
          event.preventDefault();
          setActiveIndex(activeTabIndex);
        }}
        tabs={[
          { href: 'https://pinterest.com', text: 'Boards for You' },
          {
            href: 'https://pinterest.com',
            text: 'Pins for You',
            indicator: 'dot',
          },
          { href: 'https://pinterest.com', text: 'Following', indicator: 3 },
          {
            href: 'https://pinterest.com',
            text: 'People to Follow',
            indicator: 112,
          },
        ]}
        wrap
      />
    </Flex>
  );
}

Background color

import { useState } from 'react';
import { Box, Flex, Label, Switch, Tabs, Text } from 'gestalt';

export default function Example() {
  const [activeIndex, setActiveIndex] = useState(0);
  const [isTransparent, setIsTransparent] = useState(false);

  return (
    <Flex
      alignItems="center"
      direction="column"
      gap={4}
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Flex gap={{ row: 4, column: 0 }}>
        <Label htmlFor="color">
          <Text>Transparent background</Text>
        </Label>
        <Switch
          id="color"
          onChange={() => setIsTransparent((value) => !value)}
          switched={isTransparent}
        />
      </Flex>

      <Box borderStyle="sm" color="secondary" paddingX={3} paddingY={1}>
        <Tabs
          activeTabIndex={activeIndex}
          bgColor={isTransparent ? 'transparent' : 'default'}
          onChange={({ activeTabIndex, event }) => {
            event.preventDefault();
            setActiveIndex(activeTabIndex);
          }}
          tabs={[
            { href: 'https://pinterest.com', text: 'Boards for You' },
            { href: 'https://pinterest.com', text: 'Pins for You' },
            { href: 'https://pinterest.com', text: 'Following' },
            { href: 'https://pinterest.com', text: 'People to Follow' },
          ]}
          wrap
        />
      </Box>
    </Flex>
  );
}

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

Link
Link is used to navigate to different areas of the product or to external sites. Link is the preferred component in cases where you want to direct the user to unrelated content.

SegmentedControl
SegmentedControl is used to switch between views within a small area of content, such as a Popover. SegmentedControl is preferred when changing state or selection within a view.