---
title: Tabs
description: Organize content into tabbed views with animated transitions and indicators.
links:
  source_native: tabs/tabs.tsx
  styles_native: tabs/tabs.styles.ts
---

<NativeVideoPlayerView
  target="auto"
  srcLight="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/native/components/videos/tabs-docs-light.mp4"
  srcDark="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/native/components/videos/tabs-docs-dark.mp4"
/>

## Import

```tsx
import { Tabs } from 'heroui-native';
```

## Anatomy

```tsx
<Tabs>
  <Tabs.List>
    <Tabs.ScrollView>
      <Tabs.Indicator />
      <Tabs.Trigger>
        <Tabs.Label>...</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Separator />
      <Tabs.Trigger>
        <Tabs.Label>...</Tabs.Label>
      </Tabs.Trigger>
    </Tabs.ScrollView>
  </Tabs.List>
  <Tabs.Content>...</Tabs.Content>
</Tabs>
```

- **Tabs**: Main container that manages tab state and selection. Controls active tab, handles value changes, and provides context to child components.
- **Tabs.List**: Container for tab triggers. Groups triggers together with optional styling variants (primary or secondary).
- **Tabs.ListBackground**: Optional theme-aware background container rendered behind the list surface. Mounted automatically for the `primary` variant when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop on `Tabs.List`.
- **Tabs.ScrollView**: Optional scrollable wrapper for tab triggers. Enables horizontal scrolling when tabs overflow with automatic centering of active tab.
- **Tabs.Trigger**: Interactive button for each tab. Handles press events to change active tab and measures its position for indicator animation.
- **Tabs.Label**: Text content for tab triggers. Displays the tab title with appropriate styling.
- **Tabs.Indicator**: Animated visual indicator for active tab. Smoothly transitions between tabs using spring or timing animations.
- **Tabs.Separator**: Visual separator between tabs. Shows when the current tab value is not in the `betweenValues` array, with animated opacity transitions.
- **Tabs.Content**: Container for tab panel content. Shows content when its value matches the active tab.

## Usage

### Basic Usage

The Tabs component uses compound parts to create navigable content sections.

```tsx
<Tabs value="tab1" onValueChange={setActiveTab}>
  <Tabs.List>
    <Tabs.Indicator />
    <Tabs.Trigger value="tab1">
      <Tabs.Label>Tab 1</Tabs.Label>
    </Tabs.Trigger>
    <Tabs.Trigger value="tab2">
      <Tabs.Label>Tab 2</Tabs.Label>
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="tab1">...</Tabs.Content>
  <Tabs.Content value="tab2">...</Tabs.Content>
</Tabs>
```

### Primary Variant

Default rounded primary style for tab triggers.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab} variant="primary">
  <Tabs.List>
    <Tabs.Indicator />
    <Tabs.Trigger value="settings">
      <Tabs.Label>Settings</Tabs.Label>
    </Tabs.Trigger>
    <Tabs.Trigger value="profile">
      <Tabs.Label>Profile</Tabs.Label>
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="settings">...</Tabs.Content>
  <Tabs.Content value="profile">...</Tabs.Content>
</Tabs>
```

### Secondary Variant

Underline style indicator for a more minimal appearance.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab} variant="secondary">
  <Tabs.List>
    <Tabs.Indicator />
    <Tabs.Trigger value="overview">
      <Tabs.Label>Overview</Tabs.Label>
    </Tabs.Trigger>
    <Tabs.Trigger value="analytics">
      <Tabs.Label>Analytics</Tabs.Label>
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="overview">...</Tabs.Content>
  <Tabs.Content value="analytics">...</Tabs.Content>
</Tabs>
```

### Scrollable Tabs

Handle many tabs with horizontal scrolling.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab}>
  <Tabs.List>
    <Tabs.ScrollView scrollAlign="center">
      <Tabs.Indicator />
      <Tabs.Trigger value="tab1">
        <Tabs.Label>First Tab</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Trigger value="tab2">
        <Tabs.Label>Second Tab</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Trigger value="tab3">
        <Tabs.Label>Third Tab</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Trigger value="tab4">
        <Tabs.Label>Fourth Tab</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Trigger value="tab5">
        <Tabs.Label>Fifth Tab</Tabs.Label>
      </Tabs.Trigger>
    </Tabs.ScrollView>
  </Tabs.List>
  <Tabs.Content value="tab1">...</Tabs.Content>
  <Tabs.Content value="tab2">...</Tabs.Content>
  <Tabs.Content value="tab3">...</Tabs.Content>
  <Tabs.Content value="tab4">...</Tabs.Content>
  <Tabs.Content value="tab5">...</Tabs.Content>
</Tabs>
```

### Disabled Tabs

Disable specific tabs to prevent interaction.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab}>
  <Tabs.List>
    <Tabs.Indicator />
    <Tabs.Trigger value="active">
      <Tabs.Label>Active</Tabs.Label>
    </Tabs.Trigger>
    <Tabs.Trigger value="disabled" isDisabled>
      <Tabs.Label>Disabled</Tabs.Label>
    </Tabs.Trigger>
    <Tabs.Trigger value="another">
      <Tabs.Label>Another</Tabs.Label>
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="active">...</Tabs.Content>
  <Tabs.Content value="another">...</Tabs.Content>
</Tabs>
```

### With Icons

Combine icons with labels for enhanced visual context.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab}>
  <Tabs.List>
    <Tabs.Indicator />
    <Tabs.Trigger value="home">
      <Icon name="home" size={16} />
      <Tabs.Label>Home</Tabs.Label>
    </Tabs.Trigger>
    <Tabs.Trigger value="search">
      <Icon name="search" size={16} />
      <Tabs.Label>Search</Tabs.Label>
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="home">...</Tabs.Content>
  <Tabs.Content value="search">...</Tabs.Content>
</Tabs>
```

### With Render Function

Use a render function on `Tabs.Trigger` to access state and customize content based on selection.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab}>
  <Tabs.List>
    <Tabs.Indicator />
    <Tabs.Trigger value="settings">
      {({ isSelected, value, isDisabled }) => (
        <Tabs.Label
          className={isSelected ? 'text-accent font-medium' : 'text-foreground'}
        >
          Settings
        </Tabs.Label>
      )}
    </Tabs.Trigger>
    <Tabs.Trigger value="profile">
      {({ isSelected }) => (
        <>
          <Icon name="user" size={16} />
          <Tabs.Label className={isSelected ? 'text-accent' : 'text-muted'}>
            Profile
          </Tabs.Label>
        </>
      )}
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="settings">...</Tabs.Content>
  <Tabs.Content value="profile">...</Tabs.Content>
</Tabs>
```

### With Separators

Add visual separators between tabs that show when the active tab is not between specified values.

```tsx
<Tabs value={activeTab} onValueChange={setActiveTab}>
  <Tabs.List>
    <Tabs.ScrollView>
      <Tabs.Indicator />
      <Tabs.Trigger value="general">
        <Tabs.Label>General</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Separator betweenValues={['general', 'notifications']} />
      <Tabs.Trigger value="notifications">
        <Tabs.Label>Notifications</Tabs.Label>
      </Tabs.Trigger>
      <Tabs.Separator betweenValues={['notifications', 'profile']} />
      <Tabs.Trigger value="profile">
        <Tabs.Label>Profile</Tabs.Label>
      </Tabs.Trigger>
    </Tabs.ScrollView>
  </Tabs.List>
  <Tabs.Content value="general">...</Tabs.Content>
  <Tabs.Content value="notifications">...</Tabs.Content>
  <Tabs.Content value="profile">...</Tabs.Content>
</Tabs>
```

## Example

```tsx
import {
  Button,
  Checkbox,
  Description,
  ControlField,
  Label,
  Tabs,
  TextField,
} from 'heroui-native';
import { useState } from 'react';
import { View, Text } from 'react-native';
import Animated, {
  FadeIn,
  FadeOut,
  LinearTransition,
} from 'react-native-reanimated';

const AnimatedContentContainer = ({
  children,
}: {
  children: React.ReactNode;
}) => (
  <Animated.View
    entering={FadeIn.duration(200)}
    exiting={FadeOut.duration(200)}
    className="gap-6"
  >
    {children}
  </Animated.View>
);

export default function TabsExample() {
  const [activeTab, setActiveTab] = useState('general');

  const [showSidebar, setShowSidebar] = useState(true);
  const [accountActivity, setAccountActivity] = useState(true);
  const [name, setName] = useState('');

  return (
    <Tabs value={activeTab} onValueChange={setActiveTab} variant="primary">
      <Tabs.List>
        <Tabs.ScrollView>
          <Tabs.Indicator />
          <Tabs.Trigger value="general">
            <Tabs.Label>General</Tabs.Label>
          </Tabs.Trigger>
          <Tabs.Trigger value="notifications">
            <Tabs.Label>Notifications</Tabs.Label>
          </Tabs.Trigger>
          <Tabs.Trigger value="profile">
            <Tabs.Label>Profile</Tabs.Label>
          </Tabs.Trigger>
        </Tabs.ScrollView>
      </Tabs.List>

      <Animated.View
        layout={LinearTransition.duration(200)}
        className="px-4 py-6 border border-border rounded-xl"
      >
        <Tabs.Content value="general">
          <AnimatedContentContainer>
            <ControlField
              isSelected={showSidebar}
              onSelectedChange={setShowSidebar}
            >
              <ControlField.Indicator variant="checkbox" />
              <View className="flex-1">
                <Label>Show sidebar</Label>
                <Description>Display the sidebar navigation panel</Description>
              </View>
            </ControlField>
          </AnimatedContentContainer>
        </Tabs.Content>

        <Tabs.Content value="notifications">
          <AnimatedContentContainer>
            <ControlField
              isSelected={accountActivity}
              onSelectedChange={setAccountActivity}
            >
              <ControlField.Indicator variant="checkbox" />
              <View className="flex-1">
                <Label>Account activity</Label>
                <Description>
                  Notifications about your account activity
                </Description>
              </View>
            </ControlField>
          </AnimatedContentContainer>
        </Tabs.Content>

        <Tabs.Content value="profile">
          <AnimatedContentContainer>
            <TextField isRequired>
              <Label>Name</Label>
              <Input
                value={name}
                onChangeText={setName}
                placeholder="Enter your full name"
              />
            </TextField>
            <Button size="sm" className="self-start">
              <Button.Label>Update profile</Button.Label>
            </Button>
          </AnimatedContentContainer>
        </Tabs.Content>
      </Animated.View>
    </Tabs>
  );
}
```

You can find more examples in the [GitHub repository](<https://github.com/heroui-inc/heroui-native/blob/main/example/src/app/(home)/components/tabs.tsx>).

## API Reference

### Tabs

| prop            | type                         | default     | description                                                                               |
| --------------- | ---------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
| `children`      | `React.ReactNode`            | -           | Children elements to be rendered inside tabs                                              |
| `value`         | `string`                     | -           | Currently active tab value                                                                |
| `variant`       | `'primary' \| 'secondary'`   | `'primary'` | Visual variant of the tabs                                                                |
| `className`     | `string`                     | -           | Additional CSS classes for the container                                                  |
| `animation`     | `"disable-all" \| undefined` | `undefined` | Animation configuration. Use `"disable-all"` to disable all animations including children |
| `onValueChange` | `(value: string) => void`    | -           | Callback when the active tab changes                                                      |
| `...ViewProps`  | `ViewProps`                  | -           | All standard React Native View props are supported                                        |

### Tabs.List

| prop           | type              | default | description                                        |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children`     | `React.ReactNode` | -       | Children elements to be rendered inside the list   |
| `className`    | `string`          | -       | Additional CSS classes                             |
| `background` | `React.ReactNode` | - | Background layer behind the list surface. `undefined` renders the theme-aware default for the `primary` variant; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps`       | -       | All standard React Native View props are supported |

### Tabs.ListBackground

Absolute-fill container rendered behind the primary variant's list surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.

| prop           | type              | default | description                                    |
| -------------- | ----------------- | ------- | ---------------------------------------------- |
| `children`     | `React.ReactNode` | -       | Custom content inside the background container |
| `className`    | `string`          | -       | Additional CSS classes                         |
| `...ViewProps` | `ViewProps`       | -       | All standard View props are supported          |

### Tabs.ScrollView

| prop                        | type                                     | default    | description                                              |
| --------------------------- | ---------------------------------------- | ---------- | -------------------------------------------------------- |
| `children`                  | `React.ReactNode`                        | -          | Children elements to be rendered inside the scroll view  |
| `scrollAlign`               | `'start' \| 'center' \| 'end' \| 'none'` | `'center'` | Scroll alignment variant for the selected item           |
| `className`                 | `string`                                 | -          | Additional CSS classes for the scroll view               |
| `contentContainerClassName` | `string`                                 | -          | Additional CSS classes for the content container         |
| `...ScrollViewProps`        | `ScrollViewProps`                        | -          | All standard React Native ScrollView props are supported |

### Tabs.Trigger

| prop                | type                                                                      | default | description                                                               |
| ------------------- | ------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------- |
| `children`          | `React.ReactNode \| ((props: TabsTriggerRenderProps) => React.ReactNode)` | -       | Children elements to be rendered inside the trigger, or a render function |
| `value`             | `string`                                                                  | -       | The unique value identifying this tab                                     |
| `isDisabled`        | `boolean`                                                                 | `false` | Whether the trigger is disabled                                           |
| `className`         | `string`                                                                  | -       | Additional CSS classes                                                    |
| `...PressableProps` | `PressableProps`                                                          | -       | All standard React Native Pressable props are supported                   |

#### TabsTriggerRenderProps

When using a render function for `children`, the following props are provided:

| property     | type      | description                                |
| ------------ | --------- | ------------------------------------------ |
| `isSelected` | `boolean` | Whether this trigger is currently selected |
| `value`      | `string`  | The value of the trigger                   |
| `isDisabled` | `boolean` | Whether the trigger is disabled            |

### Tabs.Label

| prop           | type              | default | description                                        |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children`     | `React.ReactNode` | -       | Text content to be rendered as label               |
| `className`    | `string`          | -       | Additional CSS classes                             |
| `...TextProps` | `TextProps`       | -       | All standard React Native Text props are supported |

### Tabs.Indicator

| prop                    | type                     | default | description                                                  |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------------ |
| `children`              | `React.ReactNode`        | -       | Custom indicator content                                     |
| `className`             | `string`                 | -       | Additional CSS classes                                       |
| `animation`             | `TabsIndicatorAnimation` | -       | Animation configuration                                      |
| `isAnimatedStyleActive` | `boolean`                | `true`  | Whether animated styles (react-native-reanimated) are active |
| `...Animated.ViewProps` | `Animated.ViewProps`     | -       | All Reanimated Animated.View props are supported             |

#### TabsIndicatorAnimation

Animation configuration for Tabs.Indicator component. Can be:

- `false` or `"disabled"`: Disable all animations
- `true` or `undefined`: Use default animations
- `object`: Custom animation configuration

| prop                | type                                   | default                                                                      | description                                     |
| ------------------- | -------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------- |
| `state`             | `'disabled' \| boolean`                | -                                                                            | Disable animations while customizing properties |
| `width.type`        | `'spring' \| 'timing'`                 | `'spring'`                                                                   | Type of animation to use                        |
| `width.config`      | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }` (spring) or `{ duration: 200 }` (timing) | Reanimated animation configuration              |
| `height.type`       | `'spring' \| 'timing'`                 | `'spring'`                                                                   | Type of animation to use                        |
| `height.config`     | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }` (spring) or `{ duration: 200 }` (timing) | Reanimated animation configuration              |
| `translateX.type`   | `'spring' \| 'timing'`                 | `'spring'`                                                                   | Type of animation to use                        |
| `translateX.config` | `WithSpringConfig \| WithTimingConfig` | `{ stiffness: 1200, damping: 120 }` (spring) or `{ duration: 200 }` (timing) | Reanimated animation configuration              |

### Tabs.Separator

| prop                    | type                     | default | description                                                                                                                            |
| ----------------------- | ------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `betweenValues`         | `string[]`               | -       | Array of tab values between which the separator should be visible. The separator shows when the current tab value is NOT in this array |
| `isAlwaysVisible`       | `boolean`                | `false` | If true, opacity is always 1 regardless of the current tab value                                                                       |
| `className`             | `string`                 | -       | Additional CSS classes                                                                                                                 |
| `animation`             | `TabsSeparatorAnimation` | -       | Animation configuration                                                                                                                |
| `isAnimatedStyleActive` | `boolean`                | `true`  | Whether animated styles (react-native-reanimated) are active                                                                           |
| `children`              | `React.ReactNode`        | -       | Custom separator content                                                                                                               |
| `...Animated.ViewProps` | `Animated.ViewProps`     | -       | All Reanimated Animated.View props are supported                                                                                       |

**Note:** The following style properties are occupied by animations and cannot be set via className:

- `opacity` - Animated for separator visibility transitions (0 when current tab is in `betweenValues`, 1 when not)

To customize these properties, use the `animation` prop. To completely disable animated styles and use your own via className or style prop, set `isAnimatedStyleActive={false}`.

#### TabsSeparatorAnimation

Animation configuration for Tabs.Separator component. Can be:

- `false` or `"disabled"`: Disable all animations
- `true` or `undefined`: Use default animations
- `object`: Custom animation configuration

| prop                   | type                    | default             | description                                     |
| ---------------------- | ----------------------- | ------------------- | ----------------------------------------------- |
| `state`                | `'disabled' \| boolean` | -                   | Disable animations while customizing properties |
| `opacity.value`        | `[number, number]`      | `[0, 1]`            | Opacity values [hidden, visible]                |
| `opacity.timingConfig` | `WithTimingConfig`      | `{ duration: 200 }` | Animation timing configuration                  |

### Tabs.Content

| prop           | type              | default | description                                         |
| -------------- | ----------------- | ------- | --------------------------------------------------- |
| `children`     | `React.ReactNode` | -       | Children elements to be rendered inside the content |
| `value`        | `string`          | -       | The value of the tab this content belongs to        |
| `className`    | `string`          | -       | Additional CSS classes                              |
| `...ViewProps` | `ViewProps`       | -       | All standard React Native View props are supported  |

## Hooks

### useTabs

Hook to access tabs root context values within custom components or compound components.

```tsx
import { useTabs } from 'heroui-native';

const CustomComponent = () => {
  const { value, onValueChange, nativeID } = useTabs();
  // ... your implementation
};
```

**Returns:** `UseTabsReturn`

| property        | type                      | description                                |
| --------------- | ------------------------- | ------------------------------------------ |
| `value`         | `string`                  | Currently active tab value                 |
| `onValueChange` | `(value: string) => void` | Callback function to change the active tab |
| `nativeID`      | `string`                  | Unique identifier for the tabs instance    |

**Note:** This hook must be used within a `Tabs` component. It will throw an error if called outside of the tabs context.

### useTabsMeasurements

Hook to access tab measurements context values for managing tab trigger positions and dimensions.

```tsx
import { useTabsMeasurements } from 'heroui-native';

const CustomIndicator = () => {
  const { measurements, variant } = useTabsMeasurements();
  // ... your implementation
};
```

**Returns:** `UseTabsMeasurementsReturn`

| property          | type                                                    | description                                       |
| ----------------- | ------------------------------------------------------- | ------------------------------------------------- |
| `measurements`    | `Record<string, ItemMeasurements>`                      | Record of measurements for each tab trigger       |
| `setMeasurements` | `(key: string, measurements: ItemMeasurements) => void` | Function to update measurements for a tab trigger |
| `variant`         | `'primary' \| 'secondary'`                              | Visual variant of the tabs                        |

#### ItemMeasurements

| property | type     | description                         |
| -------- | -------- | ----------------------------------- |
| `width`  | `number` | Width of the tab trigger in pixels  |
| `height` | `number` | Height of the tab trigger in pixels |
| `x`      | `number` | X position of the tab trigger       |

**Note:** This hook must be used within a `Tabs` component. It will throw an error if called outside of the tabs context.

### useTabsTrigger

Hook to access tab trigger context values within custom components or compound components.

```tsx
import { useTabsTrigger } from 'heroui-native';

const CustomLabel = () => {
  const { value, isSelected, nativeID } = useTabsTrigger();
  // ... your implementation
};
```

**Returns:** `UseTabsTriggerReturn`

| property     | type      | description                                |
| ------------ | --------- | ------------------------------------------ |
| `value`      | `string`  | The value of this trigger                  |
| `nativeID`   | `string`  | Unique identifier for this trigger         |
| `isSelected` | `boolean` | Whether this trigger is currently selected |

**Note:** This hook must be used within a `Tabs.Trigger` component. It will throw an error if called outside of the trigger context.
