# Tabs

Sections of content shown one at a time.

Source: [tabs.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/tabs.tsx)

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

export default function TabsDefault() {
  return (
    <Tabs defaultValue="overview" className="w-full max-w-lg">
      <TabsList>
        <TabsTrigger value="overview">Overview</TabsTrigger>
        <TabsTrigger value="tasks">Tasks</TabsTrigger>
        <TabsTrigger value="files">Files</TabsTrigger>
        <TabsTrigger value="archive" disabled>
          Archive
        </TabsTrigger>
      </TabsList>
      <TabsContent
        value="overview"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Website redesign is 64% complete with 10 tasks remaining.
      </TabsContent>
      <TabsContent
        value="tasks"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        28 tasks, 4 assigned to you.
      </TabsContent>
      <TabsContent
        value="files"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        12 files, last uploaded 2 hours ago.
      </TabsContent>
    </Tabs>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/tabs
```

## Usage

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

export function ProjectTabs() {
  return (
    <Tabs defaultValue="overview">
      <TabsList>
        <TabsTrigger value="overview">Overview</TabsTrigger>
        <TabsTrigger value="tasks">Tasks</TabsTrigger>
      </TabsList>
      <TabsContent value="overview">A summary of the project.</TabsContent>
      <TabsContent value="tasks">Everything left to do.</TabsContent>
    </Tabs>
  );
}
```

Each `TabsTrigger` shows the `TabsContent` with the same `value`. Set `defaultValue` on `Tabs`, or
control it with `value` and `onValueChange`.

## Composition

- `Tabs`
  - `TabsList`
    - `TabsTrigger`
  - `TabsContent`

## Examples

### Line

`variant="line"` underlines the active tab, for a page's main sections. When the tabs don't fit,
the row scrolls and fades at its edges.

```tsx
import { ActivityIcon, RocketIcon, SettingsIcon, UsersIcon } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

export default function TabsLine() {
  return (
    <Tabs defaultValue="activity" className="w-full">
      <TabsList variant="line">
        <TabsTrigger value="activity">
          <ActivityIcon />
          Activity
        </TabsTrigger>
        <TabsTrigger value="deployments">
          <RocketIcon />
          Deployments
        </TabsTrigger>
        <TabsTrigger value="members">
          <UsersIcon />
          Members
          <Badge variant="muted">12</Badge>
        </TabsTrigger>
        <TabsTrigger value="settings">
          <SettingsIcon />
          Settings
        </TabsTrigger>
      </TabsList>
      <TabsContent
        value="activity"
        className="mt-2 border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Sam Rivera moved “Update pricing page” to In progress.
      </TabsContent>
      <TabsContent
        value="deployments"
        className="mt-2 border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        The latest deployment finished 3 minutes ago.
      </TabsContent>
      <TabsContent
        value="members"
        className="mt-2 border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        12 people have access to this project.
      </TabsContent>
      <TabsContent
        value="settings"
        className="mt-2 border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Rename the project, change its visibility or archive it.
      </TabsContent>
    </Tabs>
  );
}
```

### Vertical

`orientation="vertical"` stacks the tabs beside their content.

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

export default function TabsVertical() {
  return (
    <Tabs defaultValue="general" orientation="vertical" className="w-full max-w-lg">
      <TabsList className="w-32">
        <TabsTrigger value="general">General</TabsTrigger>
        <TabsTrigger value="members">Members</TabsTrigger>
        <TabsTrigger value="billing">Billing</TabsTrigger>
        <TabsTrigger value="security">Security</TabsTrigger>
      </TabsList>
      <TabsContent
        value="general"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Name, URL and default language.
      </TabsContent>
      <TabsContent
        value="members"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Invite people and manage roles.
      </TabsContent>
      <TabsContent
        value="billing"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Plan, invoices and billing address.
      </TabsContent>
      <TabsContent
        value="security"
        className="border-[0.5px] border-border-muted p-4 text-muted-foreground"
      >
        Two-step verification and active sessions.
      </TabsContent>
    </Tabs>
  );
}
```

## Props

| Part | Extends |
| --- | --- |
| `Tabs` | [`Tabs.Root`](https://base-ui.com/react/components/tabs#root) |
| `TabsList` | [`Tabs.List`](https://base-ui.com/react/components/tabs#list) |
| `TabsTrigger` | [`Tabs.Tab`](https://base-ui.com/react/components/tabs#tab) |
| `TabsContent` | [`Tabs.Panel`](https://base-ui.com/react/components/tabs#panel) |

The tables leave out `className`, `style` and `render`. Parts built on Base UI take them as its [styling](https://base-ui.com/react/handbook/styling) and [composition](https://base-ui.com/react/handbook/composition) guides describe.

### Tabs

| Prop             | Type                                                                            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ---------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `value?`         | `any`                                                                           | The value of the currently active `Tab`. Use when the component is controlled. When the value is `null`, no Tab will be active.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `defaultValue?`  | `any`                                                                           | The default value. Use when the component is not controlled. When the value is `null`, no Tab will be active. Default: `0`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `orientation?`   | `"horizontal" \| "vertical"`                                                    | The component orientation (layout flow direction). Default: `"horizontal"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `onValueChange?` | `((value: Tabs.Tab.Value, eventDetails: Tabs.Root.ChangeEventDetails) => void)` | Callback invoked when new value is being set. The event `reason` is `'none'` for user-initiated changes, such as a click or keyboard navigation; `'initial'` for the first automatic selection or fallback in uncontrolled roots when `defaultValue` is omitted or `undefined`, including when the implicit initial value is disabled or missing; `'disabled'` for automatic fallback when the selected tab becomes disabled in uncontrolled roots; or `'missing'` for automatic fallback when the selected tab is removed, or when an explicit `defaultValue` never matches a mounted tab in uncontrolled roots. For automatic changes, the selected value can be `null` when no enabled Tab is available as a fallback. Automatic changes cannot be canceled; calling `eventDetails.cancel()` for `'initial'`, `'disabled'`, or `'missing'` has no effect. |

### TabsList

| Prop               | Type                  | Description                                                                                                                                                                 |
| ------------------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `activateOnFocus?` | `boolean`             | Whether to automatically change the active tab on arrow key focus. Otherwise, tabs will be activated using <kbd>Enter</kbd> or <kbd>Space</kbd> key press. Default: `false` |
| `loopFocus?`       | `boolean`             | Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. Default: `true`                                       |
| `variant?`         | `"default" \| "line"` | Default: `"default"`                                                                                                                                                        |

### TabsTrigger

| Prop            | Type      | Description                                                                                                                                                                                                                                                                                                                                                                                                |
| --------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `value`         | `any`     | The value of the Tab.                                                                                                                                                                                                                                                                                                                                                                                      |
| `disabled?`     | `boolean` | Whether the Tab is disabled. If a first Tab on a `<Tabs.List>` is disabled, it won't initially be selected. Instead, the next enabled Tab will be selected. However, it does not work like this during server-side rendering, as it is not known during pre-rendering which Tabs are disabled. To work around it, ensure that `defaultValue` or `value` on `<Tabs.Root>` is set to an enabled Tab's value. |
| `nativeButton?` | `boolean` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). Default: `true`                                                                                                                                                                                                          |

### TabsContent

| Prop           | Type      | Description                                                                                      |
| -------------- | --------- | ------------------------------------------------------------------------------------------------ |
| `value`        | `any`     | The value of the TabPanel. It will be shown when the Tab with the corresponding value is active. |
| `keepMounted?` | `boolean` | Whether to keep the HTML element in the DOM while the panel is hidden. Default: `false`          |
