Tabs
Tabs organize content into distinct categories, aiding navigation between groups of information within the same screen.
Tabs facilitate the organization of content into clear sections without requiring page navigation. Bug On MD3 Expressive Tabs support two variants: primary (indicator scales to content) and secondary (full-width indicator with a divider). Both variants support icons, horizontal scrolling, full keyboard navigation, and integrated Badges.
Introduction
The MD3 Expressive Tab component is designed for high-density navigation. It provides a clear visual hierarchy for switching between views within the same context. It supports both high-emphasis (Primary) and medium-emphasis (Secondary) styles, and is built on top of Radix UI's Tabs primitives for a reliable foundation.
Anatomy
- Tab Bar: The horizontal container for the tab list.
- Tab Item: An individual destination, containing a label and an optional icon.
- Active Indicator: A horizontal line that highlights the currently selected tab.
- Badge (Optional): A small indicator for counts or status updates attached to a tab.
- Content Panel: The area that displays the content associated with the active tab.
Variants
Primary Tabs
Primary tabs use a short indicator line that matches the width of the content (icon or label). They are typically used for the main navigation within a view.
Secondary Tabs
Secondary tabs use a full-width indicator line and include a divider at the base. They are used for sub-navigation or filtering content within a primary tab.
Features
Icons & Layouts
Tabs support two icon layouts:
- Stacked: Icon appears above the label (64dp height).
- Inline: Icon appears next to the label (48dp height). Use the
inlineIconprop to enable this.
Scrollable Tabs
When the number of tabs exceeds the available horizontal space, set scrollable={true}. The system handles the scroll-into-view behavior for active tabs automatically.
Integrated Badges
Badges are automatically positioned based on the tab layout (stacked vs inline).
Usage
Basic Usage
import {
Tabs,
TabsList,
Tab,
TabsContent
} from "@bug-on/m3-expressive";
export function Example() {
return (
<Tabs defaultValue="home">
<TabsList variant="primary">
<Tab value="home">Home</Tab>
<Tab value="profile">Profile</Tab>
<Tab value="settings">Settings</Tab>
</TabsList>
<TabsContent value="home">Home view...</TabsContent>
<TabsContent value="profile">Profile view...</TabsContent>
<TabsContent value="settings">Settings view...</TabsContent>
</Tabs>
);
}
Best Practices
Do
- Use Primary tabs for the main categorical navigation on a page.
- Use Secondary tabs for sub-sections or filters.
- Keep labels short and descriptive.
- Group tabs logically and order them by importance or frequency of use.
Don't
- Don't use tabs for navigation that takes the user to a completely different page (use a Navigation Rail or Bar instead).
- Don't stack multiple levels of tabs (e.g., Primary tabs inside Primary tabs).
- Avoid using more than 6-7 tabs in a non-scrollable list.
Accessibility
- Keyboard: Uses a roving tabindex. Navigate with Left/Right Arrow keys.
- Roles: Correctly implements
role="tablist",role="tab", androle="tabpanel". - States: Uses
aria-selectedandaria-controlsto link tabs to their content. - RTL: Automatically flips navigation directions in right-to-left layouts.
API Reference
Tabs
Root container.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled value. |
defaultValue | string | — | Initial value. |
onValueChange | (val: string) => void | — | Value change callback. |
autoActivate | boolean | false | Activates tab on focus. |
TabsList
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | — | Required. Visual style. |
scrollable | boolean | false | Enables horizontal scrolling. |
Tab
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Required. Unique tab identifier. |
icon | ReactNode | — | Optional icon. |
inlineIcon | boolean | false | Align icon horizontally. |
badge | ReactNode | — | Optional Badge component. |
disabled | boolean | false | Disables tab. |