Skip to main content

Tabs

A set of layered sections of content that can be navigated through tabs.

Import

import { Tabs } from "@soft-ui/react/tabs"

Anatomy

<Tabs.Root variant="stroke" size="m">
  <Tabs.List>
    <Tabs.Trigger value="tab1" leadingIcon={<Icon />}>Tab 1</Tabs.Trigger>
    <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
    <Tabs.Indicator />
  </Tabs.List>
  <Tabs.Content value="tab1">Content 1</Tabs.Content>
  <Tabs.Content value="tab2">Content 2</Tabs.Content>
</Tabs.Root>

Tabs.Root

The root container for the tabs component.
variant
'stroke' | 'pill' | 'pill-emphasized'
default:"'stroke'"
Visual style variant of the tabs
  • stroke: Underline indicator below tabs
  • pill: Pill-shaped background indicator
  • pill-emphasized: Pill-shaped background with accent color
size
's' | 'm'
default:"'m'"
Size of the tabs
defaultValue
string
The value of the tab that should be active by default
value
string
The controlled value of the active tab
onValueChange
(value: string) => void
Callback fired when the active tab changes
orientation
'horizontal' | 'vertical'
default:"'horizontal'"
The orientation of the tabs
activationMode
'automatic' | 'manual'
default:"'automatic'"
Whether tabs are activated automatically on focus or require explicit activation
unsafeClassName
string
Explicit escape hatch for intentional structural overrides

Data Attributes

  • data-slot="tabs"
  • data-variant: Current variant (stroke, pill, pill-emphasized)
  • data-size: Current size (s, m)

Tabs.List

Container for the tab triggers and indicator.
unsafeClassName
string
Explicit escape hatch for intentional structural overrides

Data Attributes

  • data-slot="tabs-list"

Tabs.Trigger

A button that activates a tab panel.
value
string
required
Unique identifier for this tab
leadingIcon
React.ReactNode
Icon displayed before the tab label
disabled
boolean
default:"false"
Whether the tab is disabled
unsafeClassName
string
Explicit escape hatch for intentional structural overrides

Data Attributes

  • data-slot="tabs-trigger"
  • data-active: Present when the tab is active
  • data-disabled: Present when the tab is disabled

Tabs.Indicator

Visual indicator showing the active tab.
unsafeClassName
string
Explicit escape hatch for intentional structural overrides

Data Attributes

  • data-slot="tabs-indicator"

Tabs.Content

Container for the content of a tab panel.
value
string
required
The value of the tab that this content is associated with
unsafeClassName
string
Explicit escape hatch for intentional structural overrides

Data Attributes

  • data-slot="tabs-content"

Examples

Basic Tabs

<Tabs.Root defaultValue="home">
  <Tabs.List>
    <Tabs.Trigger value="home">Home</Tabs.Trigger>
    <Tabs.Trigger value="profile">Profile</Tabs.Trigger>
    <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
    <Tabs.Indicator />
  </Tabs.List>
  <Tabs.Content value="home">Home content</Tabs.Content>
  <Tabs.Content value="profile">Profile content</Tabs.Content>
  <Tabs.Content value="settings">Settings content</Tabs.Content>
</Tabs.Root>

Pill Variant with Icons

<Tabs.Root variant="pill" defaultValue="overview">
  <Tabs.List>
    <Tabs.Trigger value="overview" leadingIcon={<HomeIcon />}>
      Overview
    </Tabs.Trigger>
    <Tabs.Trigger value="analytics" leadingIcon={<ChartIcon />}>
      Analytics
    </Tabs.Trigger>
    <Tabs.Indicator />
  </Tabs.List>
  <Tabs.Content value="overview">Overview content</Tabs.Content>
  <Tabs.Content value="analytics">Analytics content</Tabs.Content>
</Tabs.Root>

Controlled Tabs

const [value, setValue] = React.useState("first")

return (
  <Tabs.Root value={value} onValueChange={setValue}>
    <Tabs.List>
      <Tabs.Trigger value="first">First</Tabs.Trigger>
      <Tabs.Trigger value="second">Second</Tabs.Trigger>
      <Tabs.Indicator />
    </Tabs.List>
    <Tabs.Content value="first">First tab content</Tabs.Content>
    <Tabs.Content value="second">Second tab content</Tabs.Content>
  </Tabs.Root>
)

Build docs developers (and LLMs) love