The Basics

What it is

Tabs let users switch among multiple pieces of related content without reloading the web page. Tabs displays one section of content at a time and provides quick access to the other sections, making it ideal for handling a lot of content on a single page. The content section names are displayed in a single row for navigation, so users don’t have to scroll to get to the other parts of the content.

How it works

  • On load, the first tab label is selected, and its content is displayed. Depending on your use case, a different tab can be set to display on load.
  • To navigate to and activate another tab, the user clicks anywhere on that tab label. If a tab is in focus, these keys can be used:
    • Left and right arrow keys
    • Home to go to the first tab label
    • End to go to the last tab label
  • When the user activates another tab, the previous tab’s content is hidden and the new tab’s content is displayed. There is no animation, and the rest of the page doesn’t reload.

When to use

  • To group related content sections that users only need to see one at a time
  • To help users find specific information in a collection of content
  • To make it easier to read long content on mobile devices

When not to use

  • For important information, because users won’t see it if they don’t activate that tab
  • For screens where a simpler page structure would work (like headings and body text)
  • For a single content panel

What to use instead

Accordion

Use Accordion to group related content with longer labels.

Menu

Use Menu for navigation among pages or sections.

Tree

Use Tree for left-hand navigation among pages or sections.

How to use

Tabs elements

The Tabs component consists of 2 elements:

  • Navigation tab labels that display the name of each tab
  • Tab “panes” that contain the content itself

Responsive behavior for tab labels

The tab labels’ layout is dynamic, based on screen size and container width:

  • By default, the tab labels are displayed horizontally in a single row.
  • Narrow screens or containers switch to a collapsed layout. Only the selected tab label is displayed, and activating it shows a Select menu with the full list of labels.

If the set of tab labels is very wide (either because there are many tab labels or because their text is long), it can negatively affect the dropdown’s usability. To avoid this:

  • Limit the number of tabs (6 or fewer)
  • Keep tab label text short (1 or 2 words)

Tab content panes

Content panes can accommodate a range of content: text, images, forms, embedded media like videos or maps, and other interactive elements. Long content can affect usability, however: scrolling all the way to the bottom of long content often pushes the navigation tab labels out of view.

Navigation with buttons or links

Users typically activate the tab labels to navigate among the content panes. Tabs can also be designed to allow navigation via secondary controls, like Buttons or links. We recommend using Buttons inside or below the panes. They’re usually placed at the bottom of the content section, so users don’t have to scroll back to the top to switch tabs. This approach is useful for content that users are likely to view in order (usually with “Next” and “Previous” buttons).

Style

Design details

The Tabs component includes:

  • Navigation tab labels, which appear at the top of the collection of content panes, aligned left. A divider (styled using CSS, not a graphic) spanning the full width of the screen or container sits below the labels and separates them from the content.
  • Content panes, which span the full width of the screen or container. They have 24px padding by default, which can be turned off.

Tab labels layout

Tab labels appear in 2 different layouts, depending on the width of the container:

  • Horizontal (default): Labels are displayed horizontally in a single row.
  • Collapsed: This layout is applied automatically when the container’s width is smaller than the total combined width of the tab labels. Only the selected tab label is displayed. Activating it shows the full list of labels in a Select menu.

Placement and hierarchy

No additional information for this component.

Content

Tab labels

Keep tab label text as short as possible (1-2 words each). Use title case (“Meds List”, not “Meds list”). Use nouns that indicate what the pane contains (“Allergies”, not “View Allergies”).

Labels can contain custom content, such as icons and plain or formatted text (bold, italics). There are few restrictions on custom content in tab labels, but we recommend purely visual elements like icons, Badge, and Indicator. Keep these points in mind:

  • If using icons, use 16px icons and apply them to all tab labels in the group. They should appear to the left of label text.
  • Badge should appear to the right of label text.
  • Indicator should appear to the left of label text.

Don’t use interactive elements like links, Tooltips, or Buttons in tab labels, because they interfere with how the labels work.

Demos

Default Share

Multiple Inputs Share

Controlled Share

Coding

Developer tips

Tabs content

Tabs content should always be implemented using one or more TabPane subcomponents (see Props tables). The tab labels are taken from the TabPane label props.

The mountedWhileHidden prop allows TabPane content to persist if the user navigates to a different tab. This is useful if the tabs contain forms, and you want user-entered values to remain even if the user switches tabs. Set mountedWhileHidden = {true} to make tab content persist; otherwise, the content renders again with state reset when users navigate to a different tab and come back.

Selected index

The selectedIndex prop sets only the initially open TabPane. It gets overridden when users navigate to other tabs. After the component has been mounted, you can’t change the selected tab just by changing the selectedIndex prop. For programmatic control over the selected tab, use the event handler onTabsChange.

Automatic switch to collapsed layout

Tab labels are displayed horizontally in a single row by default. When the sum of the minimum width of all labels exceeds the available width of the Tabs parent element, the tab labels switch to a collapsed layout. Only the selected tab’s label is displayed, and activating it shows a Select menu with the full list of labels.

Repository

Implementation links

Tabs directory in Bitbucket

TabPane directory in Bitbucket

Implementation details

It is strongly recommended to familiarize yourself with the Forge source code. While this documentation is a best effort to document the intent and usage of a component, sometimes some features only become clear when looking at the source code. Also, looking at Forge's source code may help identify and fix bugs in either your application or Forge itself.

Storybook files

Forge maintains at least one storybook file per component. While the primary audience for these files is typically the Forge team, these storybook files may cover usages of the component not covered by a demo. The storybook for the latest version of forge can be found at go/forge-storybook.

Testing library

Forge strongly encourages using testing-library to write tests for your application.

"The more your tests resemble the way your software is used, the more confidence they can give you."

If you're having trouble testing a Forge component using testing-library, it would be a good idea to see how Forge tests its own components. For the most part, Forge tries to use screen.getByRole as much as it can, as that API provides the best feedback on a11y compliance. Forge discourages the use of document.querySelector and screen.getByTestId as both APIs encourage using implementation details to test your component, and discourage adding roles to your component.

With that being said, many of Forge's components were not built with accessability in mind. These components do break the recommendations listed above.

Import statements

In Nimbus applications

athenaOne serves the Forge bundle independently from your application's bundle. Importing Forge components directly from '@athena/forge' takes advantage of this feature.

import { Tabs, TabPane } from '@athena/forge'

In standalone applications

Importing components using the exact path to the module takes advantage of webpack's tree shaking feature. Webpack will include only that module and its dependencies.

import Tabs from '@athena/forge/Tabs';
import TabPane from '@athena/forge/TabPane';

To use this import guidance, Typescript applications must use typescript >= 4.7.3, and should add this setting to their tsconfig.json file:

{
"compilerOptions": {
"moduleResolution": "Node16",
}
}

If this setting doesn't work for your application, use this import statement instead:

import Tabs from '@athena/forge/dist/Tabs';
import TabPane from '@athena/forge/dist/TabPane';

Props