

<Preview name="BasicTabsExample" />

## Overview [#overview]

The **Tabs** component organises related content into separate panels that users can switch between, providing an efficient way to present multiple views or sections within the same interface area. Built on Base UI primitives, it offers full keyboard navigation and accessibility features.

## Usage [#usage]

```tsx
import {
  Tabs,
  TabsList,
  TabsTrigger,
  TabsContent,
} from "@tilt-legal/cubitt-components/primitives";
```

```tsx
<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="settings">Settings</TabsTrigger>
    <TabsTrigger value="billing">Billing</TabsTrigger>
  </TabsList>
  <TabsContent value="account">Account content</TabsContent>
  <TabsContent value="settings">Settings content</TabsContent>
  <TabsContent value="billing">Billing content</TabsContent>
</Tabs>
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Using Cubitt's URL-state hooks, you can sync the tabs state with the URL by providing a `paramName`:

    ```tsx
    // The selected value will be synced with ?tab=account or ?tab=settings
    <Tabs paramName="tab" defaultValue="account">
      <TabsList variant="line">
        <TabsTrigger value="account">Account</TabsTrigger>
        <TabsTrigger value="settings">Settings</TabsTrigger>
        <TabsTrigger value="billing">Billing</TabsTrigger>
      </TabsList>
      <TabsContent value="account">Account content</TabsContent>
      <TabsContent value="settings">Settings content</TabsContent>
      <TabsContent value="billing">Billing content</TabsContent>
    </Tabs>

    // Advanced options:
    <Tabs
      paramName="section"
      paramClearOnDefault={true}    // Remove param when value equals default
      paramDebounce={300}           // Debounce URL updates
      onUrlValueChange={(value) => console.log('Active tab:', value)}
      defaultValue="home"
    >
      {/* ... */}
    </Tabs>
    ```
  </Accordion>

  <Accordion title="Polymorphic Rendering">
    `TabsTrigger` forwards Base UI's polymorphic `render` support. When the trigger renders as something other than a native `<button>` such as a router link, pass `nativeButton={false}` so keyboard and ARIA behavior stay correct.

    ```tsx
    import { Link } from "@tanstack/react-router";
    import {
      Tabs,
      TabsContent,
      TabsList,
      TabsTrigger,
    } from "@tilt-legal/cubitt-components/primitives";

    <Tabs defaultValue="overview">
      <TabsList>
        <TabsTrigger
          value="overview"
          nativeButton={false}
          render={<Link href="/overview" />}
        >
          Overview
        </TabsTrigger>
        <TabsTrigger value="usage">Usage</TabsTrigger>
      </TabsList>
      <TabsContent value="overview">Overview content</TabsContent>
      <TabsContent value="usage">Usage content</TabsContent>
    </Tabs>
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### Default [#default]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="BasicTabsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Tabs,
      TabsList,
      TabsTrigger,
      TabsContent,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <Tabs defaultValue="account" className="w-full max-w-md">
          <TabsList>
            <TabsTrigger value="account">Account</TabsTrigger>
            <TabsTrigger value="settings">Settings</TabsTrigger>
            <TabsTrigger value="billing">Billing</TabsTrigger>
          </TabsList>
          <TabsContent value="account">
            <p className="text-sm text-muted-foreground">
              Manage your account settings and preferences.
            </p>
          </TabsContent>
          <TabsContent value="settings">
            <p className="text-sm text-muted-foreground">
              Configure your application settings.
            </p>
          </TabsContent>
          <TabsContent value="billing">
            <p className="text-sm text-muted-foreground">
              View and manage your billing information.
            </p>
          </TabsContent>
        </Tabs>
      );
    }
    ```
  </Tab>
</Tabs>

### Line [#line]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="LineTabsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Tabs,
      TabsList,
      TabsTrigger,
      TabsContent,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <Tabs defaultValue="account" className="w-full max-w-md">
          <TabsList variant="line">
            <TabsTrigger value="account">Account</TabsTrigger>
            <TabsTrigger value="settings">Settings</TabsTrigger>
            <TabsTrigger value="billing">Billing</TabsTrigger>
          </TabsList>
          <TabsContent value="account">
            <p className="text-sm text-muted-foreground">
              Manage your account settings and preferences.
            </p>
          </TabsContent>
          <TabsContent value="settings">
            <p className="text-sm text-muted-foreground">
              Configure your application settings.
            </p>
          </TabsContent>
          <TabsContent value="billing">
            <p className="text-sm text-muted-foreground">
              View and manage your billing information.
            </p>
          </TabsContent>
        </Tabs>
      );
    }
    ```
  </Tab>
</Tabs>

### With Separator [#with-separator]

Use `TabsSeparator` inside the default track when you need a visual split between groups of tabs. The separator fades out while the selected pill touches either side of it, then reappears once the indicator moves away.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="TabsSeparatorExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Tabs,
      TabsContent,
      TabsList,
      TabsSeparator,
      TabsTrigger,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <Tabs defaultValue="account" className="w-full max-w-md">
          <TabsList>
            <TabsTrigger value="account">Account</TabsTrigger>
            <TabsTrigger value="settings">Settings</TabsTrigger>
            <TabsSeparator />
            <TabsTrigger value="billing">Billing</TabsTrigger>
          </TabsList>
          <TabsContent value="account">Account content</TabsContent>
          <TabsContent value="settings">Settings content</TabsContent>
          <TabsContent value="billing">Billing content</TabsContent>
        </Tabs>
      );
    }
    ```
  </Tab>
</Tabs>

### Sizes [#sizes]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="TabsSizesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Tabs, TabsList, TabsTrigger } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <div className="flex flex-col items-center gap-12 py-6 w-full">
          <Tabs defaultValue="account">
            <TabsList size="sm" variant="line">
              <TabsTrigger value="account">Account</TabsTrigger>
              <TabsTrigger value="settings">Settings</TabsTrigger>
              <TabsTrigger value="billing">Billing</TabsTrigger>
            </TabsList>
          </Tabs>
          <Tabs defaultValue="account">
            <TabsList size="md" variant="line">
              <TabsTrigger value="account">Account</TabsTrigger>
              <TabsTrigger value="settings">Settings</TabsTrigger>
              <TabsTrigger value="billing">Billing</TabsTrigger>
            </TabsList>
          </Tabs>
          <Tabs defaultValue="account">
            <TabsList size="lg" variant="line">
              <TabsTrigger value="account">Account</TabsTrigger>
              <TabsTrigger value="settings">Settings</TabsTrigger>
              <TabsTrigger value="billing">Billing</TabsTrigger>
            </TabsList>
          </Tabs>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Disabled [#disabled]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DisabledTabsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Tabs,
      TabsList,
      TabsTrigger,
      TabsContent,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <Tabs defaultValue="account" className="w-full max-w-md">
          <TabsList variant="line">
            <TabsTrigger value="account">Account</TabsTrigger>
            <TabsTrigger value="settings" disabled>
              Settings
            </TabsTrigger>
            <TabsTrigger value="billing">Billing</TabsTrigger>
          </TabsList>
          <TabsContent value="account">
            <p className="text-sm text-muted-foreground">
              Account settings are available.
            </p>
          </TabsContent>
          <TabsContent value="billing">
            <p className="text-sm text-muted-foreground">
              Billing information is here.
            </p>
          </TabsContent>
        </Tabs>
      );
    }
    ```
  </Tab>
</Tabs>

### Animated Height [#animated-height]

Combine tabs with the `AutoResize` component for smooth height transitions between tab panels.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="AnimatedHeightTabsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Tabs,
      TabsList,
      TabsTrigger,
      TabsContent,
      AutoResize,
      Skeleton,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <Tabs defaultValue="account" className="w-full max-w-md">
          <TabsList className="mb-4" size="sm" variant="line">
            <TabsTrigger value="account">Account</TabsTrigger>
            <TabsTrigger value="settings">Settings</TabsTrigger>
            <TabsTrigger value="billing">Billing</TabsTrigger>
          </TabsList>

          <div className="bg-muted/50 text-card-foreground flex flex-col gap-4 rounded-lg border border-border/30 p-6">
            <AutoResize>
              <TabsContent value="account">
                <div className="flex flex-col gap-6">
                  <div className="flex gap-4">
                    <Skeleton className="h-12 w-12 rounded-full" />
                    <div className="flex-1 flex flex-col justify-center gap-2">
                      <Skeleton className="h-4 w-2/3" />
                      <Skeleton className="h-4 w-1/3" />
                    </div>
                  </div>
                  <div className="flex flex-col gap-2">
                    <Skeleton className="h-4" />
                    <Skeleton className="h-4 w-2/3" />
                  </div>
                </div>
              </TabsContent>

              <TabsContent value="settings">
                <div className="flex flex-col gap-6">
                  <div className="flex flex-col gap-2">
                    <Skeleton className="h-8 w-1/3 mb-2" />
                    <Skeleton className="h-4" />
                    <Skeleton className="h-4 w-2/3" />
                  </div>
                  <div className="flex gap-4 justify-between">
                    <div className="flex-1 flex flex-col justify-center gap-2">
                      <Skeleton className="h-4 w-2/3" />
                      <Skeleton className="h-4 w-1/3" />
                    </div>
                    <Skeleton className="size-6" />
                  </div>
                </div>
              </TabsContent>

              <TabsContent value="billing">
                <div className="flex flex-col gap-6">
                  <div className="flex flex-col gap-2">
                    <Skeleton className="h-8 w-2/3 mb-2" />
                    <Skeleton className="h-4" />
                    <Skeleton className="h-4 w-3/4" />
                  </div>
                  <div className="flex flex-col gap-2">
                    <Skeleton className="h-6 w-1/3" />
                    <div className="flex items-center justify-between gap-4">
                      <Skeleton className="h-4 w-2/3" />
                      <Skeleton className="h-6 w-10" />
                    </div>
                  </div>
                </div>
              </TabsContent>
            </AutoResize>
          </div>
        </Tabs>
      );
    }
    ```
  </Tab>
</Tabs>

### URL State [#url-state]

This example syncs with the URL parameter "demo-tab". Try switching tabs and refreshing the page or sharing the URL.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="TabsURLStateExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Tabs,
      TabsList,
      TabsTrigger,
      TabsContent,
      AutoResize,
      Skeleton,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <Tabs
          paramName="demo-tab"
          defaultValue="account"
          className="w-full max-w-md"
        >
          <TabsList className="mb-4" size="sm" variant="line">
            <TabsTrigger value="account">Account</TabsTrigger>
            <TabsTrigger value="settings">Settings</TabsTrigger>
            <TabsTrigger value="billing">Billing</TabsTrigger>
          </TabsList>

          <div className="bg-muted/50 text-card-foreground flex flex-col gap-4 rounded-lg border border-border/30 p-6">
            <AutoResize>
              <TabsContent value="account">
                {/* Account content with varying heights */}
              </TabsContent>
              <TabsContent value="settings">
                {/* Settings content with different height */}
              </TabsContent>
              <TabsContent value="billing">{/* Billing content */}</TabsContent>
            </AutoResize>
          </div>
        </Tabs>
      );
    }
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### Tabs [#tabs]

The root component for creating tabbed interfaces with multiple variants and sizes.

| Prop            | Type                      | Default | Description                                    |
| --------------- | ------------------------- | ------- | ---------------------------------------------- |
| `defaultValue`  | `string`                  | —       | The default active tab when uncontrolled.      |
| `value`         | `string`                  | —       | The controlled active tab value.               |
| `onValueChange` | `(value: string) => void` | —       | Callback fired when the active tab changes.    |
| `disabled`      | `boolean`                 | `false` | Whether all tabs are disabled.                 |
| `className`     | `string`                  | —       | Additional CSS classes for the tabs container. |

#### URL State Props [#url-state-props]

When provided with a `paramName`, the tabs will sync their state with URL parameters via TanStack Router search params.

| Prop                  | Type                      | Default | Description                                                                  |
| --------------------- | ------------------------- | ------- | ---------------------------------------------------------------------------- |
| `paramName`           | `string`                  | —       | URL parameter name for syncing state with URL. Enables URL state management. |
| `onUrlValueChange`    | `(value: string) => void` | —       | Callback when URL parameter value changes.                                   |
| `paramClearOnDefault` | `boolean`                 | `true`  | Remove URL param when value equals default.                                  |
| `paramDebounce`       | `number`                  | —       | Debounce URL updates in milliseconds.                                        |
| `paramThrottle`       | `number`                  | —       | Throttle URL updates in milliseconds.                                        |

### TabsList [#tabslist]

Container for tab triggers with variant and size controls.

| Prop        | Type                       | Default     | Description                                                                        |
| ----------- | -------------------------- | ----------- | ---------------------------------------------------------------------------------- |
| `variant`   | `"default"` \| `"line"`    | `"default"` | The visual variant of the tabs list. `default` uses the segmented control styling. |
| `size`      | `"sm"` \| `"md"` \| `"lg"` | `"md"`      | The size of the tabs.                                                              |
| `className` | `string`                   | —           | Additional CSS classes for the tabs list.                                          |

### TabsTrigger [#tabstrigger]

Individual tab trigger that users click to switch panels. Supports Base UI's polymorphic `render` prop for custom elements such as router links.

| Prop           | Type                       | Default | Description                                                                     |
| -------------- | -------------------------- | ------- | ------------------------------------------------------------------------------- |
| `value`        | `string`                   | —       | The value of the tab trigger (required).                                        |
| `disabled`     | `boolean`                  | `false` | Whether the tab trigger is disabled.                                            |
| `render`       | `ReactElement \| function` | —       | Render the trigger as a custom element or component while preserving tab state. |
| `nativeButton` | `boolean`                  | `true`  | Set to `false` when `render` outputs a non-button element like `Link` or `a`.   |
| `className`    | `string`                   | —       | Additional CSS classes for the tab trigger.                                     |

### TabsContent [#tabscontent]

The content panel for each tab.

| Prop        | Type     | Description                                 |
| ----------- | -------- | ------------------------------------------- |
| `value`     | `string` | The value of the tab content (required).    |
| `className` | `string` | Additional CSS classes for the tab content. |
