> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.

# Tabs

Switches between a few peer sections of one page without going anywhere — `Tabs` owns the panels, which is what separates it from `SegmentedControl`. Underline tabs are the default for a page’s primary division; reach for `SegmentedTabs` for scoped views of one collection or a second level below these, `SegmentedControl` for a toggle that owns no panel, and `Accordion` when the sections should be open at once.

[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Tabs) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/navigation-tabs--overview)

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Basic = () => (
  <div className='w-600'>
    <Tabs defaultValue='1' data-testid='tabs'>
      <TabsList>
        <TabsTrigger value='1'>Tab 1</TabsTrigger>
        <TabsTrigger value='2'>Tab 2</TabsTrigger>
        <TabsTrigger value='3'>Tab 3</TabsTrigger>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  </div>
);
```

## Usage

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';
```

## Examples

### Variants

`grayscale` mutes the active underline and label for a bar that should sit quieter than the content it introduces.

```tsx
import { VStack } from '@wallarm-org/design-system/Stack';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Variants = () => (
  <div className='w-600'>
    <VStack gap={32}>
      <Tabs defaultValue='1'>
        <TabsList>
          <TabsTrigger value='1'>Tab 1 (Default)</TabsTrigger>
          <TabsTrigger value='2'>Tab 2 (Default)</TabsTrigger>
          <TabsTrigger value='3'>Tab 3 (Default)</TabsTrigger>
        </TabsList>
        <TabsContent value='1'>Content for Tab 1</TabsContent>
        <TabsContent value='2'>Content for Tab 2</TabsContent>
        <TabsContent value='3'>Content for Tab 3</TabsContent>
      </Tabs>

      <Tabs defaultValue='1' variant='grayscale'>
        <TabsList>
          <TabsTrigger value='1'>Tab 1 (Grayscale)</TabsTrigger>
          <TabsTrigger value='2'>Tab 2 (Grayscale)</TabsTrigger>
          <TabsTrigger value='3'>Tab 3 (Grayscale)</TabsTrigger>
        </TabsList>
        <TabsContent value='1'>Content for Tab 1</TabsContent>
        <TabsContent value='2'>Content for Tab 2</TabsContent>
        <TabsContent value='3'>Content for Tab 3</TabsContent>
      </Tabs>
    </VStack>
  </div>
);
```

### Icons

An icon before the label, for a set where the words alone are slow to scan.

```tsx
import { CircleDashed } from '@wallarm-org/design-system/icons';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Icons = () => (
  <div className='w-600'>
    <Tabs defaultValue='1'>
      <TabsList>
        <TabsTrigger value='1'>
          <CircleDashed />
          Tab 1
        </TabsTrigger>
        <TabsTrigger value='2'>
          Tab 2
          <CircleDashed />
        </TabsTrigger>
        <TabsTrigger value='3'>
          <CircleDashed />
          Tab 3
          <CircleDashed />
        </TabsTrigger>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  </div>
);
```

### Icons only

Icons with no labels — only for a set the reader already knows, and each trigger still needs an accessible name.

```tsx
import { Earth, LayoutDashboard, Search } from '@wallarm-org/design-system/icons';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const IconsOnly = () => (
  <div className='w-600'>
    <Tabs defaultValue='1'>
      <TabsList>
        <TabsTrigger value='1'>
          <LayoutDashboard />
        </TabsTrigger>
        <TabsTrigger value='2'>
          <Search />
        </TabsTrigger>
        <TabsTrigger value='3'>
          <Earth />
        </TabsTrigger>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  </div>
);
```

### Badge

A count in the trigger, which is what turns a tab into a status: how much is waiting behind it.

```tsx
import { CircleDashed } from '@wallarm-org/design-system/icons';
import { NumericBadge } from '@wallarm-org/design-system/NumericBadge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Badge = () => (
  <div className='w-600'>
    <Tabs defaultValue='1'>
      <TabsList>
        <TabsTrigger value='1'>
          <CircleDashed />
          Tab 1<NumericBadge>2 </NumericBadge>
        </TabsTrigger>
        <TabsTrigger value='2'>
          Tab 2<NumericBadge>2 </NumericBadge>
        </TabsTrigger>
        <TabsTrigger value='3'>
          <CircleDashed />
          Tab 3
          <CircleDashed />
          <NumericBadge>2 </NumericBadge>
        </TabsTrigger>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  </div>
);
```

### Sizes

`medium` and `small`. `Tabs` is the one in the switcher family with a size — the segmented pills are fixed height.

```tsx
import { VStack } from '@wallarm-org/design-system/Stack';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Sizes = () => (
  <div className='w-600'>
    <VStack gap={32}>
      <Tabs defaultValue='1' size='medium'>
        <TabsList>
          <TabsTrigger value='1'>Tab 1</TabsTrigger>
          <TabsTrigger value='2'>Tab 2</TabsTrigger>
          <TabsTrigger value='3'>Tab 3</TabsTrigger>
        </TabsList>
        <TabsContent value='1'>Content for Tab 1</TabsContent>
        <TabsContent value='2'>Content for Tab 2</TabsContent>
        <TabsContent value='3'>Content for Tab 3</TabsContent>
      </Tabs>

      <Tabs defaultValue='1' size='small'>
        <TabsList>
          <TabsTrigger value='1'>Tab 1</TabsTrigger>
          <TabsTrigger value='2'>Tab 2</TabsTrigger>
          <TabsTrigger value='3'>Tab 3</TabsTrigger>
        </TabsList>
        <TabsContent value='1'>Content for Tab 1</TabsContent>
        <TabsContent value='2'>Content for Tab 2</TabsContent>
        <TabsContent value='3'>Content for Tab 3</TabsContent>
      </Tabs>
    </VStack>
  </div>
);
```

### Scrollable

Past the available width the list scrolls sideways with arrow buttons rather than wrapping, so the bar stays one line however many tabs there are.

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Scrollable = () => (
  <div className='w-400'>
    <Tabs defaultValue='1'>
      <TabsList>
        {new Array(20).fill(null).map((_, index) => {
          const key = index + 1;
          return (
            <TabsTrigger key={key} value={String(key)}>
              Tab {key}
            </TabsTrigger>
          );
        })}
      </TabsList>

      {new Array(20).fill(null).map((_, index) => {
        const key = index + 1;
        return (
          <TabsContent key={key} value={String(key)}>
            Content for Tab {key}
          </TabsContent>
        );
      })}
    </Tabs>
  </div>
);
```

### Controlled

`value` plus `onChange` when something outside the tabs decides which one is open — a route, or a step in a flow.

```tsx
import { useState } from 'react';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Controlled = () => {
  const [value, setValue] = useState('1');

  return (
    <Tabs value={value} onChange={setValue}>
      <TabsList>
        <TabsTrigger value='1'>Tab 1</TabsTrigger>
        <TabsTrigger value='2'>Tab 2</TabsTrigger>
        <TabsTrigger value='3'>Tab 3</TabsTrigger>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  );
};
```

### Disabled

A disabled trigger keeps its place but drops out of the keyboard order. Prefer hiding a tab that has nothing behind it.

```tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const Disabled = () => (
  <Tabs defaultValue='1'>
    <TabsList>
      <TabsTrigger value='1'>Tab 1</TabsTrigger>
      <TabsTrigger value='2' disabled>
        Tab 2 (Disabled)
      </TabsTrigger>
      <TabsTrigger value='3'>Tab 3</TabsTrigger>
      <TabsTrigger value='4'>Tab 4</TabsTrigger>
    </TabsList>
    <TabsContent value='1'>Content for Tab 1</TabsContent>
    <TabsContent value='2'>Content for Tab 2</TabsContent>
    <TabsContent value='3'>Content for Tab 3</TabsContent>
    <TabsContent value='4'>Content for Tab 4</TabsContent>
  </Tabs>
);
```

### With separator

`TabsSeparator` groups the triggers inside one bar, for when two of them belong together more than the rest.

```tsx
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@wallarm-org/design-system/DropdownMenu';
import { Ellipsis } from '@wallarm-org/design-system/icons';
import { Tabs, TabsButton, TabsContent, TabsList, TabsSeparator, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const WithSeparator = () => (
  <div className='w-600'>
    <Tabs defaultValue='1'>
      <TabsList>
        <TabsTrigger value='1'>Tab 1</TabsTrigger>
        <TabsTrigger value='2'>Tab 2</TabsTrigger>
        <TabsTrigger value='3'>Tab 3</TabsTrigger>
        <TabsSeparator />
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <TabsButton>
              <Ellipsis />
              More
            </TabsButton>
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem>Action 1</DropdownMenuItem>
            <DropdownMenuItem>Action 2</DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  </div>
);
```

### With line actions

`TabsLineActions` puts controls on the right of the bar itself, which is where an action about the whole section belongs rather than inside a panel.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@wallarm-org/design-system/DropdownMenu';
import { CircleDashed, Ellipsis } from '@wallarm-org/design-system/icons';
import { Tabs, TabsButton, TabsContent, TabsLineActions, TabsList, TabsSeparator, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const WithLineActions = () => (
  <div className='w-600'>
    <Tabs defaultValue='1'>
      <TabsList>
        <TabsTrigger value='1'>Tab 1</TabsTrigger>
        <TabsTrigger value='2'>Tab 2</TabsTrigger>
        <TabsTrigger value='3'>Tab 3</TabsTrigger>
        <TabsSeparator />
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <TabsButton>
              <Ellipsis />
              More
            </TabsButton>
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem>Action 1</DropdownMenuItem>
            <DropdownMenuItem>Action 2</DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>

        <TabsLineActions>
          <Button variant='ghost' color='neutral' size='medium'>
            <CircleDashed />
          </Button>

          <TabsSeparator mx={8} />

          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant='secondary' color='brand' size='medium'>
                <CircleDashed />
                Button
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent>
              <DropdownMenuItem>Action 1</DropdownMenuItem>
              <DropdownMenuItem>Action 2</DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </TabsLineActions>
      </TabsList>
      <TabsContent value='1'>Content for Tab 1</TabsContent>
      <TabsContent value='2'>Content for Tab 2</TabsContent>
      <TabsContent value='3'>Content for Tab 3</TabsContent>
    </Tabs>
  </div>
);
```

## Props

| Prop          | Type                                     | Default | Description |
| ------------- | ---------------------------------------- | ------- | ----------- |
| size          | `TabsSize \| undefined`                  |         |             |
| variant       | `TabsVariant \| undefined`               |         |             |
| value         | `string \| undefined`                    |         |             |
| defaultValue  | `string \| undefined`                    |         |             |
| lazyMount     | `boolean \| undefined`                   | `true`  |             |
| unmountOnExit | `boolean \| undefined`                   | `true`  |             |
| asChild       | `boolean \| undefined`                   | `false` |             |
| onChange      | `((value: string) => void) \| undefined` |         |             |
| data-testid   | `string \| undefined`                    |         |             |

## Explorer

