Tabs

Organize content into tabbed views with animated transitions and indicators.

Import

import { Tabs } from 'heroui-native';

Anatomy

<Tabs>
  <Tabs.List>
    <Tabs.ScrollView>
      <Tabs.Indicator />
      <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 (pill or line).
  • 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.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.

<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>

Pill Variant

Default rounded pill style for tab triggers.

<Tabs value={activeTab} onValueChange={setActiveTab} variant="pill">
  <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>

Line Variant

Underline style indicator for a more minimal appearance.

<Tabs value={activeTab} onValueChange={setActiveTab} variant="line">
  <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.

<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.

<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.

<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.

<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>

Example

import { Tabs, TextField, FormField, Checkbox, Button } 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="pill">
      <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>
            <FormField
              isSelected={showSidebar}
              onSelectedChange={setShowSidebar}
            >
              <FormField.Indicator variant="checkbox" />
              <View className="flex-1">
                <FormField.Label>Show sidebar</FormField.Label>
                <FormField.Description>
                  Display the sidebar navigation panel
                </FormField.Description>
              </View>
            </FormField>
          </AnimatedContentContainer>
        </Tabs.Content>

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

        <Tabs.Content value="profile">
          <AnimatedContentContainer>
            <TextField isRequired>
              <TextField.Label>Name</TextField.Label>
              <TextField.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>
  );
}

API Reference

Tabs

proptypedefaultdescription
childrenReact.ReactNode-Children elements to be rendered inside tabs
valuestring-Currently active tab value
variant'pill' | 'line''pill'Visual variant of the tabs
classNamestring-Additional CSS classes for the container
animation"disable-all" | undefinedundefinedAnimation configuration. Use "disable-all" to disable all animations including children
onValueChange(value: string) => void-Callback when the active tab changes
...ViewPropsViewProps-All standard React Native View props are supported

Tabs.List

proptypedefaultdescription
childrenReact.ReactNode-Children elements to be rendered inside the list
classNamestring-Additional CSS classes
...ViewPropsViewProps-All standard React Native View props are supported

Tabs.ScrollView

proptypedefaultdescription
childrenReact.ReactNode-Children elements to be rendered inside the scroll view
scrollAlign'start' | 'center' | 'end' | 'none''center'Scroll alignment variant for the selected item
classNamestring-Additional CSS classes for the scroll view
contentContainerClassNamestring-Additional CSS classes for the content container
...ScrollViewPropsScrollViewProps-All standard React Native ScrollView props are supported

Tabs.Trigger

proptypedefaultdescription
childrenReact.ReactNode | ((props: TabsTriggerRenderProps) => React.ReactNode)-Children elements to be rendered inside the trigger, or a render function
valuestring-The unique value identifying this tab
isDisabledbooleanfalseWhether the trigger is disabled
classNamestring-Additional CSS classes
...PressablePropsPressableProps-All standard React Native Pressable props are supported

TabsTriggerRenderProps

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

propertytypedescription
isSelectedbooleanWhether this trigger is currently selected
valuestringThe value of the trigger
isDisabledbooleanWhether the trigger is disabled

Tabs.Label

proptypedefaultdescription
childrenReact.ReactNode-Text content to be rendered as label
classNamestring-Additional CSS classes
...TextPropsTextProps-All standard React Native Text props are supported

Tabs.Indicator

proptypedefaultdescription
childrenReact.ReactNode-Custom indicator content
classNamestring-Additional CSS classes
animationTabsIndicatorAnimation-Animation configuration
...Animated.ViewPropsAnimated.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
proptypedefaultdescription
width.type'spring' | 'timing''spring'Type of animation to use
width.configWithSpringConfig | WithTimingConfig{ stiffness: 1200, damping: 120 } (spring) or { duration: 200 } (timing)Reanimated animation configuration
height.type'spring' | 'timing''spring'Type of animation to use
height.configWithSpringConfig | WithTimingConfig{ stiffness: 1200, damping: 120 } (spring) or { duration: 200 } (timing)Reanimated animation configuration
left.type'spring' | 'timing''spring'Type of animation to use
left.configWithSpringConfig | WithTimingConfig{ stiffness: 1200, damping: 120 } (spring) or { duration: 200 } (timing)Reanimated animation configuration

Tabs.Content

proptypedefaultdescription
childrenReact.ReactNode-Children elements to be rendered inside the content
valuestring-The value of the tab this content belongs to
classNamestring-Additional CSS classes
...ViewPropsViewProps-All standard React Native View props are supported

Hooks

useTabs

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

import { useTabs } from 'heroui-native';

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

Returns: UseTabsReturn

propertytypedescription
valuestringCurrently active tab value
onValueChange(value: string) => voidCallback function to change the active tab
nativeIDstringUnique 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.

import { useTabsMeasurements } from 'heroui-native';

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

Returns: UseTabsMeasurementsReturn

propertytypedescription
measurementsRecord<string, ItemMeasurements>Record of measurements for each tab trigger
setMeasurements(key: string, measurements: ItemMeasurements) => voidFunction to update measurements for a tab trigger
variant'pill' | 'line'Visual variant of the tabs

ItemMeasurements

propertytypedescription
widthnumberWidth of the tab trigger in pixels
heightnumberHeight of the tab trigger in pixels
xnumberX 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.

import { useTabsTrigger } from 'heroui-native';

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

Returns: UseTabsTriggerReturn

propertytypedescription
valuestringThe value of this trigger
nativeIDstringUnique identifier for this trigger
isSelectedbooleanWhether 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.

On this page