Tabs
A collection of related content sections plus a navigation element for switching among them quickly.
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
Use Accordion to group related content with longer labels.
Use Menu for navigation among pages or sections.
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
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
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-lts.
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 accessibility 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';