Components

Tabs

Layered content sections with line and pill variants in horizontal or vertical orientation.

Overview

Tabs organize related sections of content that share the same page context. Each TabsTrigger maps to one TabsContent with the same value. Use tabs when the user can switch between peer sections without leaving the page or changing the primary route.

There are two visual variants:

  • line — page-level or section-level tabs with an underline indicator.
  • pill — compact toggle-like tabs for filters, ranges, and smaller controls.

Both variants support orientation="horizontal" and orientation="vertical".

Usage

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@marmoui/ui';

⚠️ All four named exports are flatTabs, TabsList, TabsTrigger, TabsContent. There is no Tabs.List, Tabs.Trigger, or Tabs.Content sub-component API.

Examples

Line Tabs

Line tabs

Default page-level tabs for peer content sections.

Overview content here.
<Tabs defaultValue="overview">
<TabsList variant="line">
  <TabsTrigger value="overview">Overview</TabsTrigger>
  <TabsTrigger value="analytics">Analytics</TabsTrigger>
  <TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="overview">Overview content here.</TabsContent>
<TabsContent value="analytics">Analytics content here.</TabsContent>
<TabsContent value="settings">Settings content here.</TabsContent>
</Tabs>

Pill Tabs

Pill tabs

Compact tabs for filters, ranges, and dense control groups.

Last 30 days
<Tabs defaultValue="30d" variant="pill">
<TabsList>
  <TabsTrigger value="7d">7 days</TabsTrigger>
  <TabsTrigger value="30d">30 days</TabsTrigger>
  <TabsTrigger value="90d">90 days</TabsTrigger>
</TabsList>
<TabsContent value="7d">Last 7 days</TabsContent>
<TabsContent value="30d">Last 30 days</TabsContent>
<TabsContent value="90d">Last 90 days</TabsContent>
</Tabs>

Vertical Line Tabs

Vertical line tabs

Use for settings-style pages where the tab list acts like local section navigation.

Profile settings
<Tabs defaultValue="profile" orientation="vertical">
<TabsList variant="line">
  <TabsTrigger value="profile">Profile</TabsTrigger>
  <TabsTrigger value="billing">Billing</TabsTrigger>
  <TabsTrigger value="notifications">Notifications</TabsTrigger>
</TabsList>
<TabsContent value="profile">Profile settings</TabsContent>
<TabsContent value="billing">Billing settings</TabsContent>
<TabsContent value="notifications">Notification settings</TabsContent>
</Tabs>

Vertical Pill Tabs

Vertical pill tabs

Use for compact local filters or short mode switches in a side panel.

Active items
<Tabs defaultValue="active" variant="pill" orientation="vertical">
<TabsList>
  <TabsTrigger value="active">Active</TabsTrigger>
  <TabsTrigger value="paused">Paused</TabsTrigger>
  <TabsTrigger value="archived">Archived</TabsTrigger>
</TabsList>
<TabsContent value="active">Active items</TabsContent>
<TabsContent value="paused">Paused items</TabsContent>
<TabsContent value="archived">Archived items</TabsContent>
</Tabs>

Controlled Tabs

Controlled tabs

Use controlled tabs when the selected tab needs URL sync or external state.

Overview content
const [tab, setTab] = useState('overview');

<Tabs value={tab} onValueChange={setTab}>
<TabsList>
  <TabsTrigger value="overview">Overview</TabsTrigger>
  <TabsTrigger value="analytics">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="overview">Overview content</TabsContent>
<TabsContent value="analytics">Analytics content</TabsContent>
</Tabs>

Choosing the right variant

VariantUse whenAvoid when
line horizontalPage-level peer sections, profile/detail sections, analytics breakdownsTiny filter controls or dense toolbars
pill horizontalDate ranges, list filters, modes, compact dashboard controlsPrimary page navigation or many long labels
line verticalSettings/detail pages with local section navigationMain app/sidebar navigation
pill verticalShort side-panel filters or local modesMore than a few options or long-form navigation

Tabs are not breadcrumbs and they are not sidebar navigation. Use tabs only when the sections are peers inside the current page context.

Props

Tabs

PropTypeDefaultDescription
defaultValuestringThe tab selected on mount (uncontrolled)
valuestringThe controlled selected tab value
onValueChange(value: string) => voidCallback when the selected tab changes
orientation'horizontal' | 'vertical''horizontal'Orientation of the tabs
variant'line' | 'pill''line'Visual style shared with list and triggers through context
classNamestringAdditional className on the root element
--tabs-list-widthCSS variable180pxWidth of the TabsList column when orientation="vertical". Set via style or a parent class.

TabsList

PropTypeDefaultDescription
variant'line' | 'pill'inherited from TabsVisual style of the tab strip
orientation'horizontal' | 'vertical'inherited from TabsOrientation of the tab strip
classNamestringAdditional className

TabsTrigger

PropTypeDefaultDescription
valuestringrequiredValue that identifies this tab
variant'line' | 'pill'inherited from TabsVisual style of the trigger
orientation'horizontal' | 'vertical'inherited from TabsOrientation-specific trigger styling
disabledbooleanfalseDisable this tab trigger
classNamestringAdditional className

TabsContent

PropTypeDefaultDescription
valuestringrequiredThe tab value whose panel this renders
classNamestringAdditional className

Common Mistakes

  • Using namespace sub-componentsTabs.List, Tabs.Trigger, Tabs.Content do not exist. These will throw a runtime error. Always use the flat named exports:
    // ❌ WRONG — Tabs.List does not exist
    <Tabs.List>
      <Tabs.Trigger value="a">A</Tabs.Trigger>
    </Tabs.List>
    
    // ✅ CORRECT — use flat exports
    <TabsList>
      <TabsTrigger value="a">A</TabsTrigger>
    </TabsList>
  • Forgetting to import TabsList/TabsTrigger/TabsContentimport { Tabs } alone is not enough. Import all four: import { Tabs, TabsList, TabsTrigger, TabsContent } from '@marmoui/ui';
  • Mismatched values — every TabsTrigger value must match a TabsContent value exactly (case-sensitive).
  • Nesting TabsContent outside of TabsTabsContent must be a descendant of the Tabs root to receive state.
  • Using stale variantsvariant="default" and variant="underline" do not exist. Use variant="line" or variant="pill".
  • Using tabs for hierarchy — breadcrumbs and sidebar navigation are separate patterns. Tabs are for peer sections inside one page.