Skip to content
Browse documentation
DocsComponents

Tabs

Move between related views without leaving the page.

The big picture.

Installation

Terminal
pnpm dlx shadcn@latest add https://react-toolbox.io/r/tabs.json

New here? Set up your project first. Styles, fonts, and required components are included.

Usage

Control the selected tab with index and onChange. Each Tab supplies its label and panel content. Arrow keys move between enabled tabs.

Use the Code tab above for a complete example. The import follows your configured UI alias. See Theming to customize colors, spacing, and style slots.

State and callbacks

Tabs is controlled through index and onChange. Update index when the callback reports a new tab. There is no defaultIndex API.

The default hideMode="unmounted" unmounts inactive tab contents. Use hideMode="display" to preserve child component state while hiding inactive panels.

API reference

Generated from the installed TypeScript source. Defaults are the values declared by each function; a dash means no parameter default is declared. Forwarded props can use the receiving component’s default. Callback types show the exact arguments passed to your handler.

Tab

PropertyTypeDefault
activebooleanfalse
activeClassNamestring—
disabledbooleanfalse
iconReactNode—
indexnumber—
labelReactNode—
ripplebooleantrue
classNamesTabTheme

Classes for the component's rendered parts. Applied after legacy theme overrides.

—
themeDeprecated aliasTabTheme

Use classNames for style slots; use ThemeProvider for design tokens.

—
refRef<HTMLDivElement>—

Callbacks

PropertyTypeDefault
onActive() => void—
onClick(event: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>, index?: number) => void—

Also inherits Omit<HTMLAttributes<HTMLDivElement>, 'onClick'>, except the omissions and overrides in its exported props type below. Use the supported native aria-*, data-*, event, and element attributes as appropriate.

Additional defaults on inherited props: hidden = false.

Style slots

Pass a map of Tailwind classes to classNames. Available keys:

  • active
  • arrow
  • arrowContainer
  • disableAnimation
  • disabled
  • fixed
  • hidden
  • icon
  • inverse
  • label
  • navigation
  • navigationContainer
  • pointer
  • rippleWrapper
  • tab
  • tabs
  • withIcon
  • withText

theme is the deprecated compatibility alias. When both are provided, classNames wins conflicting utilities within each slot. See Theming for tokens and override rules.

Tabs

PropertyTypeDefault
disableAnimatedBottomBorderboolean—
fixedbooleanfalse
hideMode'display' | 'unmounted''unmounted'
indexnumber0
inversebooleanfalse
classNamesTabsTheme

Classes for the component's rendered parts. Applied after legacy theme overrides.

—
themeDeprecated aliasTabsTheme

Use classNames for style slots; use ThemeProvider for design tokens.

—
refRef<TabsHandle>—

Callbacks

PropertyTypeDefault
onChange(index: number) => void—

Also inherits Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>, except the omissions and overrides in its exported props type below. Use the supported native aria-*, data-*, event, and element attributes as appropriate.

Style slots

Pass a map of Tailwind classes to classNames. Available keys:

  • active
  • arrow
  • arrowContainer
  • disableAnimation
  • disabled
  • fixed
  • hidden
  • icon
  • inverse
  • label
  • navigation
  • navigationContainer
  • pointer
  • rippleWrapper
  • tab
  • tabs
  • withIcon
  • withText

theme is the deprecated compatibility alias. When both are provided, classNames wins conflicting utilities within each slot. See Theming for tokens and override rules.

TabContent

PropertyTypeDefault
activebooleanfalse
classNamesTabContentTheme

Classes for the component's rendered parts. Applied after legacy theme overrides.

—
themeDeprecated aliasTabContentTheme

Use classNames for style slots; use ThemeProvider for design tokens.

—
refRef<HTMLElement>—

Also inherits HTMLAttributes<HTMLElement>, except the omissions and overrides in its exported props type below. Use the supported native aria-*, data-*, event, and element attributes as appropriate.

Additional defaults on inherited props: hidden = true.

Style slots

Pass a map of Tailwind classes to classNames. Available keys:

  • active
  • arrow
  • arrowContainer
  • disableAnimation
  • disabled
  • fixed
  • hidden
  • icon
  • inverse
  • label
  • navigation
  • navigationContainer
  • pointer
  • rippleWrapper
  • tab
  • tabs
  • withIcon
  • withText

theme is the deprecated compatibility alias. When both are provided, classNames wins conflicting utilities within each slot. See Theming for tokens and override rules.

Exported TypeScript types

Expand a type to inspect its full declaration, including generic constraints, union branches, callback arguments, ref handles, and inherited props. Import these types from @/components/ui/material/tabs.

TabTheme
tsx
type TabTheme = TabsTheme;
TabEvent
tsx
type TabEvent = MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>;
TabProps
tsx
interface TabProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick'> {
  active?: boolean;
  activeClassName?: string;
  disabled?: boolean;
  icon?: ReactNode;
  index?: number;
  label?: ReactNode;
  onActive?: () => void;
  onClick?: (event: TabEvent, index?: number) => void;
  ripple?: boolean;
  /** Classes for the component's rendered parts. Applied after legacy theme overrides. */
  classNames?: TabTheme;
  /** @deprecated Use classNames for style slots; use ThemeProvider for design tokens. */
  theme?: TabTheme;
  ref?: Ref<HTMLDivElement>;
}
TabClassNames
tsx
type TabClassNames = TabTheme;
TabsTheme
tsx
type TabsTheme = Partial<
  Record<
    | 'disableAnimation'
    | 'active'
    | 'arrow'
    | 'arrowContainer'
    | 'disabled'
    | 'fixed'
    | 'hidden'
    | 'icon'
    | 'inverse'
    | 'label'
    | 'navigation'
    | 'navigationContainer'
    | 'pointer'
    | 'rippleWrapper'
    | 'tab'
    | 'tabs'
    | 'withIcon'
    | 'withText',
    string
  >
>;
TabsClassNames
tsx
type TabsClassNames = TabsTheme;
TabsHandle
tsx
interface TabsHandle {
  updatePointer(index?: number): void;
  readonly node: HTMLDivElement | null;
}
TabsProps
tsx
interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
  disableAnimatedBottomBorder?: boolean;
  fixed?: boolean;
  hideMode?: 'display' | 'unmounted';
  index?: number;
  inverse?: boolean;
  onChange?: (index: number) => void;
  /** Classes for the component's rendered parts. Applied after legacy theme overrides. */
  classNames?: TabsTheme;
  /** @deprecated Use classNames for style slots; use ThemeProvider for design tokens. */
  theme?: TabsTheme;
  ref?: Ref<TabsHandle>;
}
TabContentTheme
tsx
type TabContentTheme = TabsTheme;
TabContentProps
tsx
interface TabContentProps extends HTMLAttributes<HTMLElement> {
  active?: boolean;
  /** Classes for the component's rendered parts. Applied after legacy theme overrides. */
  classNames?: TabContentTheme;
  /** @deprecated Use classNames for style slots; use ThemeProvider for design tokens. */
  theme?: TabContentTheme;
  ref?: Ref<HTMLElement>;
}
TabContentClassNames
tsx
type TabContentClassNames = TabContentTheme;

Material guidelines

  • Group related peer views. Use short, centered labels and an indicator to identify the selected view; tabs should not submit actions.
  • Text-only tabs are 48px high. Fixed tabs share the available width on small screens; the M1 minimum widths are 72px on small screens and 160px on larger screens.

Material Design 1: Tabs