> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tabs

> A tabbed interface built from Tabs, TabsList, TabsTrigger and TabsContent.

```tsx theme={null}
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@fanvue/ui";
import type { TabsContentProps, TabsListProps, TabsProps, TabsTriggerProps } from "@fanvue/ui";
```

## Examples

## Default

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-tabs--default&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="260" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Tabs — Default" />
</Frame>

```tsx theme={null}
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@fanvue/ui";

<Tabs defaultValue="tab1">
  <TabsList>
    <TabsTrigger value="tab1">Photos</TabsTrigger>
    <TabsTrigger value="tab2">Videos</TabsTrigger>
    <TabsTrigger value="tab3">Posts</TabsTrigger>
  </TabsList>
  <TabsContent value="tab1">
    <p className="pt-4 text-content-tertiary text-sm">Photos content</p>
  </TabsContent>
  <TabsContent value="tab2">
    <p className="pt-4 text-content-tertiary text-sm">Videos content</p>
  </TabsContent>
  <TabsContent value="tab3">
    <p className="pt-4 text-content-tertiary text-sm">Posts content</p>
  </TabsContent>
</Tabs>
```

## Hug (sizes to content — does not stretch)

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-tabs--hug-style&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="260" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Hug (sizes to content — does not stretch)" />
</Frame>

```tsx theme={null}
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@fanvue/ui";

<div style={{ width: 400 }}>
  <Tabs defaultValue="tab1">
    <TabsList variant="hug">
      <TabsTrigger value="tab1">Photos</TabsTrigger>
      <TabsTrigger value="tab2">Videos</TabsTrigger>
      <TabsTrigger value="tab3">Posts</TabsTrigger>
    </TabsList>
    <TabsContent value="tab1">
      <p className="pt-4 text-content-tertiary text-sm">
        List is only as wide as its tabs — not 400 px
      </p>
    </TabsContent>
    <TabsContent value="tab2">
      <p className="pt-4 text-content-tertiary text-sm">Videos content</p>
    </TabsContent>
    <TabsContent value="tab3">
      <p className="pt-4 text-content-tertiary text-sm">Posts content</p>
    </TabsContent>
  </Tabs>
</div>
```

## With Disabled Tab

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-tabs--with-disabled-tab&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="260" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Tabs — With Disabled Tab" />
</Frame>

```tsx theme={null}
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@fanvue/ui";

<Tabs defaultValue="tab1">
  <TabsList>
    <TabsTrigger value="tab1">Active</TabsTrigger>
    <TabsTrigger value="tab2">Normal</TabsTrigger>
    <TabsTrigger value="tab3" disabled>
      Disabled
    </TabsTrigger>
  </TabsList>
  <TabsContent value="tab1">
    <p className="pt-4 text-content-tertiary text-sm">Active tab content</p>
  </TabsContent>
  <TabsContent value="tab2">
    <p className="pt-4 text-content-tertiary text-sm">Normal tab content</p>
  </TabsContent>
</Tabs>
```

## Scrollable - Narrow Container

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-tabs--scrollable&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="260" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Tabs — Scrollable - Narrow Container" />
</Frame>

```tsx theme={null}
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@fanvue/ui";

<div className="w-[320px]">
  <Tabs defaultValue="tab1">
    <TabsList scrollable flushLeft>
      <TabsTrigger value="tab1">Manager Insights</TabsTrigger>
      <TabsTrigger value="tab2">Chatter Leaderboard</TabsTrigger>
      <TabsTrigger value="tab3">Fans at Risk</TabsTrigger>
    </TabsList>
    <TabsContent value="tab1">
      <p className="pt-4 text-content-tertiary text-sm">
        Drag the row sideways to reach the later tabs.
      </p>
    </TabsContent>
    <TabsContent value="tab2">
      <p className="pt-4 text-content-tertiary text-sm">Chatter Leaderboard content</p>
    </TabsContent>
    <TabsContent value="tab3">
      <p className="pt-4 text-content-tertiary text-sm">Fans at Risk content</p>
    </TabsContent>
  </Tabs>
</div>
```

## All States (Matrix)

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-tabs--all-states&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="260" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Tabs — All States (Matrix)" />
</Frame>

```tsx theme={null}
import { Tabs, TabsList, TabsTrigger } from "@fanvue/ui";

<div className="flex flex-col gap-8">
  <div>
    <p className="typography-body-small-14px-semibold mb-4 text-content-tertiary">Active</p>
    <div className="flex gap-10">
      <div className="flex flex-col items-center gap-2">
        <p className="typography-description-12px-regular text-content-tertiary">Default</p>
        <Tabs defaultValue="t">
          <TabsList>
            <TabsTrigger value="t">Tab</TabsTrigger>
          </TabsList>
        </Tabs>
      </div>
      <div className="flex flex-col items-center gap-2">
        <p className="typography-description-12px-regular text-content-tertiary">Disabled</p>
        <Tabs defaultValue="t">
          <TabsList>
            <TabsTrigger value="t" disabled>
              Tab
            </TabsTrigger>
          </TabsList>
        </Tabs>
      </div>
    </div>
  </div>
  <div>
    <p className="typography-body-small-14px-semibold mb-4 text-content-tertiary">Inactive</p>
    <div className="flex gap-10">
      <div className="flex flex-col items-center gap-2">
        <p className="typography-description-12px-regular text-content-tertiary">Default</p>
        <Tabs defaultValue="other">
          <TabsList>
            <TabsTrigger value="t">Tab</TabsTrigger>
          </TabsList>
        </Tabs>
      </div>
      <div className="flex flex-col items-center gap-2">
        <p className="typography-description-12px-regular text-content-tertiary">Disabled</p>
        <Tabs defaultValue="other">
          <TabsList>
            <TabsTrigger value="t" disabled>
              Tab
            </TabsTrigger>
          </TabsList>
        </Tabs>
      </div>
    </div>
  </div>
</div>
```

## Props

## Tabs

Root container for a tabbed interface. Manages the active tab state and coordinates `TabsList`, `TabsTrigger`, and `TabsContent`.

Built on Radix UI `Tabs`.

A direct re-export of the Radix `TabsPrimitive.Root` primitive. It accepts all of that primitive's props, unchanged.

## TabsContent

Renders the content panel for a given tab `value`. Only visible when its value matches the active tab.

<ParamField path="asChild" type="boolean" />

## TabsList

Container for `TabsTrigger` elements. Renders a sliding active-tab indicator that animates between tabs.

<ParamField path="alignLeft" type="boolean | Breakpoint">
  Controls tab alignment within a full-width container.

  * `false` (default): tabs spread evenly
  * `true`: tabs left-aligned, sized to content
  * `"md"` (breakpoint): spread on mobile, left-aligned at breakpoint and up
</ParamField>

<ParamField path="asChild" type="boolean" />

<ParamField path="flushLeft" type="boolean" default="false">
  Drops the leading padding on the first tab so its label sits flush with the list's left edge, lining it up with page content above or below the bar (a heading, for instance) rather than being indented by the tab's own padding. Spacing between tabs is unaffected. Pair with `alignLeft` or `variant="hug"`.
</ParamField>

<ParamField path="fullWidth" type="boolean" default="true">
  When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing.
</ParamField>

<ParamField path="scrollable" type="boolean" default="false">
  Scrolls the row horizontally when the tabs don't fit, instead of letting them compress. By default a tab may shrink until its label ellipsises, which on a narrow viewport can truncate every label at once; here each tab keeps its natural width and the overflow scrolls. The scrollbar itself is hidden — the clipped tab at the edge is the affordance.

  Because the tabs are content-sized, they no longer spread to fill the row: a set that does fit sits left-aligned, as with `alignLeft`.

  Horizontal orientation only: a vertical list already grows downwards.
</ParamField>

<ParamField path="variant" type="&#x22;fill&#x22; | &#x22;hug&#x22;">
  Explicit layout variant. `"hug"` sizes the list to its content; `"fill"` stretches it full-width with equal-width tabs. Takes precedence over `fullWidth`/`alignLeft`.
</ParamField>

## TabsTrigger

An interactive tab that activates its associated `TabsContent` panel.

Renders a `<button>` by default. Pass `asChild` to render the tab as another element — e.g. a router link for navigation tabs (`<TabsTrigger asChild><Link href="…">…</Link></TabsTrigger>`). With `asChild` the child element becomes the tab itself (receiving `role="tab"` and focus management), so the tab stays a single interactive control rather than nesting one inside the other.

<ParamField path="asChild" type="boolean" />

## Exported types

Also exported from `@fanvue/ui`: `TabsContentProps`, `TabsListProps`, `TabsProps`, `TabsTriggerProps`.

***

**Setup:** [Installation](/docs/ui/installation) · [Theming](/docs/ui/theming)
