Tabs
Tabs organize groups of related content and let users navigate between them.
Installation
Base UI components are all available as a single package.
npm install @base_ui/reactOnce you have the package installed, import the component.
import * as Tabs from '@base_ui/react/Tabs';
Anatomy
Tabs are implemented using a collection of related components:
- <Tabs.Root />is a top-level component that wraps the Tabs List and Tab Panel components so that tabs and their panels can communicate with one another.
- <Tabs.Tab />is the tab element itself. Clicking on a tab displays its corresponding panel.
- <Tabs.List />is the container that houses the tabs. Responsible for handling focus and keyboard navigation between tabs.
- <Tabs.Panel />is the card that hosts the content associated with a tab.
- <Tabs.Indicator />is an optional active tab indicator.
<Tabs.Root>
  <Tabs.List>
    <Tabs.Tab>One</Tabs.Tab>
    <Tabs.Tab>Two</Tabs.Tab>
    <Tabs.Indicator />
  </Tabs.List>
  <Tabs.Panel>First page</Tabs.Panel>
  <Tabs.Panel>Second page</Tabs.Panel>
</Tabs.Root>
Specifying values
By default, Tab components and their corresponding panels are zero-indexed.
The first tab has a value of 0, the second tab has a value of 1, and so on.
Activating a tab opens the panel with the same value, corresponding to the order in which each component is nested within its container.
Though not required, you can add the value prop to the Tab and Tab Panel to control how these components are associated.
<Tabs.Root defaultValue={1}>
  <Tabs.List>
    <Tabs.Tab value={1}>One</Tabs.Tab>
    <Tabs.Tab value={2}>Two</Tabs.Tab>
  </Tabs.List>
  <Tabs.Panel value={1}>First page</Tabs.Panel>
  <Tabs.Panel value={2}>Second page</Tabs.Panel>
</Tabs.Root>
Indicator
Though it's optional, the Tabs.Indicator component can be added to implement a visual indicator for the active tab.
To help with styling—in particular animating its position—some CSS variables are provided.
- --active-tab-topis the distance in- pxbetween the top of the active tab and the top of the- Tabs.Panel's bounding box.
- --active-tab-bottomis the distance in- pxbetween the bottom of the active tab and the bottom of the- Tabs.Panel's bounding box.
- --active-tab-leftis the distance in- pxbetween the left-hand edge of the active tab and the left-hand edge of the- Tabs.Panel's bounding box.
- --active-tab-rightis the distance in- pxbetween the right-hand edge of the active tab and the right-hand edge of the- Tabs.Panel's bounding box.
- --active-tab-widthis the width in- pxof the active tab's bounding box.
- --active-tab-heightis the height in- pxof the active tab's bounding box.
Additionally, the Indicator has the data-activation-direction attribute representing the relation of the selected tab to the previously selected one.
Its value is one of the following:
- leftwhen the active tab is to the left of the previously active tab (only applied when- orientation=horizontal).
- rightwhen the active tab is to the right of the previously active tab (only applied when- orientation=horizontal).
- topwhen the active tab is above the previously active tab (only applied when- orientation=vertical).
- bottomwhen the active tab is below the previously active tab (only applied when- orientation=vertical).
- nonewhen there is no previously selected tab.
This example uses the CSS variables and data attributes described above to create an "elastic" movement effect.
The next example shows a differently shaped Indicator with a simpler movement.
As the transition is independent of direction, the data-activation-direction attribute is not used for styling.
Server rendering
The Indicator's rendering depends on React effects and cannot be done on the server. This means that if you're using server-side rendering (SSR), the initially rendered content will not contain the Indicator. It will appear after React hydrates the components.
If you want to minimize the time the Indicator is not visible, you can set the renderBeforeHydration prop to true.
This will make the component include an inline script that sets the CSS variables as soon as it's rendered by the browser.
<Tabs.Indicator renderBeforeHydration />
It is disabled by default, as the script contributes to the size of the payload sent by the server.
Orientation
To arrange tabs vertically, set orientation="vertical" on the <Tabs /> component.
Now, the user can navigate with the up and down arrow keys rather than the default left-to-right behavior for horizontal tabs.
Links
Tabs can be rendered as links to routes in your application. A common use case for tabs is implementing client-side navigation that doesn't require an HTTP round-trip to the server.
Manual tab activation
By default, when using keyboard navigation, tabs are activated automatically when they receive focus.
Alternatively, you can set activateOnFocus={false} on <Tabs.List> so tabs are not activated automatically when they receive focus.
<Tabs.List activateOnFocus={false} />
Overriding default components
Use the render prop to override the rendered element:
<Tabs.Tab render={<MyCustomTab />} />
// or
<Tabs.Tab render={(props) => <MyCustomTab {...props} />} />
If you provide a non-interactive element such as a <span>, the Tab components automatically add the necessary accessibility attributes.
Accessibility
Base UI Tabs follow the Tabs WAI-ARIA design pattern.
Keyboard navigation
| Keys | Descriptions | 
|---|---|
| Left Arrow | Moves focus to the previous tab (when orientation="horizontal") and activates it ifactivateOnFocusis set. | 
| Right Arrow | Moves focus to the next tab (when orientation="horizontal") and activates it ifactivateOnFocusis set. | 
| Up Arrow | Moves focus to the previous tab (when orientation="vertical") and activates it ifactivateOnFocusis set. | 
| Down Arrow | Moves focus to the next tab (when orientation="vertical") and activates it ifactivateOnFocusis set. | 
| Space, Enter | Activates the focused tab. | 
Labeling
To make the Tabs component suite accessible to assistive technology, label the <Tabs.List /> element with aria-label.
<Tabs>
  <Tabs.List aria-label="Seasons">
    <Tabs.Tab>Spring</Tabs.Tab>
    <Tabs.Tab>Summer</Tabs.Tab>
    <Tabs.Tab>Fall</Tabs.Tab>
    <Tabs.Tab>Winter</Tabs.Tab>
  </Tabs.List>
</Tabs>