MD3
Expressive
MATERIAL DESIGN 3 EXPRESSIVE

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.

Loading demo...

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.

Loading demo...

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 inlineIcon prop to enable this.
Loading demo...

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.

Loading demo...

Integrated Badges

Badges are automatically positioned based on the tab layout (stacked vs inline).

Loading demo...

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", and role="tabpanel".
  • States: Uses aria-selected and aria-controls to link tabs to their content.
  • RTL: Automatically flips navigation directions in right-to-left layouts.

API Reference

Tabs

Root container.

PropTypeDefaultDescription
valuestringControlled value.
defaultValuestringInitial value.
onValueChange(val: string) => voidValue change callback.
autoActivatebooleanfalseActivates tab on focus.

TabsList

PropTypeDefaultDescription
variant"primary" | "secondary"Required. Visual style.
scrollablebooleanfalseEnables horizontal scrolling.

Tab

PropTypeDefaultDescription
valuestringRequired. Unique tab identifier.
iconReactNodeOptional icon.
inlineIconbooleanfalseAlign icon horizontally.
badgeReactNodeOptional Badge component.
disabledbooleanfalseDisables tab.